对于01背包一维优化的一点理解: 二维转化为一维: 删掉了第一维:在前i个物品中取。 f[j]表示:拿了总体积不超过j的物品,最大总价值。
为何能转化为一维? 二维时的更新方式:fi=max(fi - 1 ,fi - 1] + w[i]); 1.我们发现,对于每次循环的下一组i,只会用到i-1来更新当前值,不会用到i-2及之前值。于是可以在这次更新的时候,将原来的更新掉,反正以后也用不到。 所以对于i的更新,只需用一个数组,直接覆盖就行了。 2.我们发现,对于每次j的更新,只需用到之前i-1时的j或者j-v[i],不会用到后面的值。 所以为了防止串着改,我们采取从后往前更新的方式,用原来i-1的数组来更新i。 (如果从前往后更新的话,前面的更新过之后,会接着更新后面的值,这样就不能保证是用原来i-1的数组来更新i的了)
如何转化为一维呢? 只用一个数组,每次都覆盖前面的数组。
1.如果当前位置的东西不拿的话,和前一位置的信息(原来i-1数组的这个位置上的值)是相同的,所以不用改变。 2.如果当前位置的东西拿了的话,需要和前一位置的信息(原来i-1数组的这个位置上值)取max。 所以,更新方式就为:f[j]=max(f[j],f[j-v[i]]+w[i]);
整个更新方式就相当于:
每次i++,就从后往前覆盖一遍f数组,看每个位置上的值是否更新。
背包问题核心
状态表示 <<<<<< $ f i$
-
集合 >>> 从前i个物品中取,且最终总体积不超过j
-
属性 >>> Max(也就是找最大值)
状态计算 <<<<<< 分为01背包,完全背包,多重背包 , 分组背包..(实际上是集合划分的过程)
01背包的情况(优化后)
#include <bits/stdc++.h> 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 = m ; j >= v[i]; j -- ) f[j] = max(f[j] , f[j-v[i]] + w[i]); } cout << f[m] << endl; return 0; }
完全背包问题(优化后)
#include <bits/stdc++.h> 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; }
多重背包问题 O( NVlogS )
把s拆分成logs组,拆完之后对所有新的物品做一遍01背包问题即可 , 二进制优化,即每组都是二的次幂。只要总和小于s就可以接着往下凑下去。
#include <bits/stdc++.h> using namespace std; const int N = 25010 , M = 2010; int w[N] , v[N]; int f[M]; int main() { int n , m ; 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]; return 0; }