首页 > 其他分享 >图解背包问题及其优化

图解背包问题及其优化

时间:2023-01-05 11:24:28浏览次数:29  
标签:背包 const int include 枚举 using 图解 优化

image

导读 ^ _ ^

背包问题是动态规划的入门经典问题。
本文将讲解四种常见的背包问题及其优化方法。

背包分类

每 件/种 物品体积Vi
不超过背包容量的总价值最大化W(不一定装满)

  • 01 背包:每件物品最多只用一次
  • 完全背包:每件物品无限个
  • 多重背包:每件物品有限ai个
  • 分组背包:从每组选择一个

01背包与动态规划思路

01动态规划.png

代码实现(二维朴素法)

image.png

#include<iostream>
#include<algorithm>
using namespace std;

const int N = 1010;

int n,m;
int v[N],w[N];//默认初始为0
int dp[N][N];

int main() {
	
	cin >> n >> m;
	for (int i = 1; i <=n; i++) cin >> v[i] >> w[i];

	for(int i = 1; i <=n; i++) 
		for(int j = 0; j <=m; j++) {
			dp[i][j] = dp[i-1][j];
			if(j>=v[i]) dp[i][j] = max(dp[i][j],dp[i-1][j-v[i]] + w[i]);
		}
	cout<<dp[n][m]<<endl;
	return 0;
}

代码实现(滚动数组,状态压缩)

Snipaste_2022-12-24_22-43-42.png

#include<iostream>
#include<algorithm>

using namespace std;

const int N = 1010;

int n,m;
int v[N],w[N];
int dp[N];

int main() {
	cin>>n>>m;
	for (int i = 1; i <= n; i++) cin>>v[i]>>w[i];
	
	for(int i = 1; i <= n; i++)
	    for(int j = m; j >= v[i]; j--) {
	    	//这里空集部分判断直接挪到循环里面
	    	//不用推导,直接继承上一层
	    	dp[j] = max(dp[j],dp[j-v[i]]+w[i]);
		}
	cout<<dp[m]<<endl;
	return 0;
}

完全背包与数列推导优化

最朴素的思路起点

Snipaste_2022-12-25_10-58-43.png

代码实现(显然会超时,1000三次方过亿次了,C++的 1s 大体运行一亿次)

image.png

#include<iostream>
#include<algorithm>

using namespace std;

const int N = 1010;

int n,m;
int v[N],w[N];
int f[N][N];

int main() {
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> v[i] >> w[i];
    
    for(int i = 1; i <= n; i++) 
       for(int j = 0; j<= m; j++) 
          for(int k = 0; k*v[i] <= j; k++)
              f[i][j] = max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
    cout << f[n][m]<<endl;
    return 0;
}

DP常见的数列推导优化

推导递推公式,常减去一项来观察规律实现降维。
image.png

#include<iostream>
#include<algorithm>

using namespace std;

const int N = 1010;

int n,m;
int v[N],w[N];
int f[N][N];

int main() {
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> v[i] >> w[i];
    
    for(int i = 1; i <= n; i++) 
       for(int j = 0; j<= m; j++) {
           f[i][j] = f[i-1][j];
           if(j >= v[i]) f[i][j] = max(f[i][j],f[i][j-v[i]]+w[i]);
       }
           
    cout << f[n][m]<<endl;
    return 0;
}

状态压缩

区分 01背包(全上层) 和 完全背包(本层+上层) 的遍历顺序。
image.png

#include<iostream>
#include<algorithm>

using namespace std;

const int N = 1010;

int n,m;
int v[N],w[N];
int f[N];

int main() {
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> v[i] >> w[i];
    
    for(int i = 1; i <= n; i++) 
       for(int j = v[i]; j<= m; j++) {
           f[j] = max(f[j],f[j-v[i]]+w[i]);
       }
           
    cout << f[m]<<endl;
    return 0;
}

多重背包与二进制枚举优化

image.png

最朴素的思路起点(如果数据过大和完全背包一样超时)

image.png

#include<iostream>
#include<algorithm>

using namespace std;

const int N = 110;

int n,m;
int v[N],w[N],s[N];
int f[N][N];

int main() {
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> v[i] >> w[i] >> s[i];
    
    for(int i = 1; i <= n; i++)
       for(int j = 0; j <= m; j++) 
          for(int k = 0; k*v[i] <= j && k <= s[i]; k++) {
              f[i][j] = max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
          }
    cout << f[n][m]<<endl;
    return 0;
}

二进制枚举优化

比如要枚举0->1023中所有的数能不能凑成其中任意一个数
我们平常的枚举方法就是:0,1,2,3,4,5,…,1023。这样枚举1024次
使用二进制枚举优化,就可以只需枚举10次就可以枚举出任意一个数。
将0~1023这1024个数分为10个组,
每组分别是:1 2 4 8 16 32 64 128 256 512 这10个数字(2^0 2^1 2^2 … 2^9)。
在枚举的时候只枚举这10个数字,选或不选。就可以枚举出0~1023中的任意一个数字
image.png
image.png
image.png

二进制枚举优化代码实现

image.png

#include<iostream>
#include<algorithm>

using namespace std;

const int N = 25000;

int n,m;
int v[N],w[N];
int f[N];

int main() {
    cin >> n >> m;
    int cnt = 0;
    for (int i = 1; i <= n; i++) {
        int a,b,s;
        cin >> a >> b >> s;
        int k = 1;
        while(k <= s) {
            cnt++;
            v[cnt] = a*k;
            w[cnt] = b*k;
            s -= k;
            k *= 2;
        }
        if(s > 0) {
            cnt++;
            v[cnt] = a*s;
            w[cnt] = b*s;
        }
    }
    n = cnt;
    for(int i = 1; i <= n; i++) 
       for(int j = m; j >= v[i]; j--)
          f[j] = max(f[j],f[j-v[i]] + w[i]);
          
    cout << f[m] << endl;
    return 0;
}

分组背包与状态压缩总结

image.png

和01背包特别像,不过多一个组内选择

image.png

状态压缩以及遍历顺序的总结

只要是上一层推导的,则从后往前
如果是同层推导的,则从前往后
只要是由上层某个方向定向推来的,就可以进行状态压缩

代码实现

#include<iostream>
#include<algorithm>

using namespace std;

const int N = 110;

int v[N][N],w[N][N],s[N];
int f[N];
int n,m;

int main() {
    cin >> n >> m;
    for(int i = 1; i <= n; i++) {
        cin >> s[i];
        for(int j = 1; j<= s[i]; j++) {
            cin >> v[i][j] >> w[i][j];
        }
    }
    
    for(int i = 1; i <= n; i++) 
       for(int j = m; j >= 0; j--)
           for(int k = 1; k <= s[i]; k++)
              if(j >= v[i][k]) f[j] = max(f[j],f[j-v[i][k]]+w[i][k]);
    cout << f[m] << endl;
    return 0;
}

谢谢你的观看!

标签:背包,const,int,include,枚举,using,图解,优化
From: https://www.cnblogs.com/HX-Note/p/17027000.html

相关文章

  • cereas学习(5) vins-fusion vio融合gps全局优化
      残差定义  Factors.h/********************************************************Copyright(C)2019,AerialRoboticsGroup,HongKongUniversityof......
  • 使用OpenMP与AVX优化矩阵乘法
    使用OpenMP与AVX优化矩阵乘法由于课设内容做的太过简(mo)单(yu),于是在去年12月初的时候就计划写三篇博客随笔作为实验报告,前两篇简单介绍了OpenMP和SIMD指令进行铺垫,本篇将会......
  • dp 优化
    dp优化\(\text{ByDaiRuiChen007}\)I.[ARC085D]-NRE\(\text{Link}\)思路分析将最终的第\(i\)对\(a_i\)和\(b_i\)打包起来,形成\(i\)个01数对\((a_i,b_i......
  • 5.Oracle的优化器
    1.oracle的优化器优化器(optimizer)是oracle数据库内置的一个核心子系统。优化器的目的是按照一定的判断原则来得到它认为的目标SQL在当前的情形下的最高效的执行路径,也......
  • 洛谷P1048 典型01背包问题
    写在前面的话蒟蒻在学习诸多图论算法之前,实际上没学过dp!强说是学过也是只学了01背包,今天就来温习一下……DP是啥?动态规划(DynamicProgramming,DP)是运筹学的一个分支,是......
  • 自研ORM Include拆分查询(递归算法 支持无限层级) 性能优化探讨
    最近我在优化 Include拆分查询,贴出源码供大家交流探讨是否还有优化空间。测试代码1Console.WriteLine($"总记录数:{db.Query<Category>().Count()}......
  • 【VSM每周观点】狭义的DevOps是一种局部优化 |第1期
    在新的一年,我们将为你提供VSM每周观点,并于每周三早上7点准时在本公众号进行发布。VSM每周观点仅代表作者对价值流管理(VSM)的个人理解和看法,同时我们会在每一期的文末推荐与当......
  • 基于Matlab求解高铁运营公司列车开行优化问题
    ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • 认知无线电网络协作频谱感知优化附matlab完整代码
    ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • 图解 Retrofit - ServiceMethod
    通过​​Retrofit+RxAndroid实践总结​​,我们已经了解到了Retrofit的基本用法,为了知其所以然,我们以图解加源码的方式从ServiceMethod入手,逐步解析Retrofit。首先......