背包问题(恰好背满 二维背包) 总结
2014-11-05 09:30
183 查看
专题训练:点击打开链接 密码:JXFEACM
他人总结:点击打开链接 背包九讲:点击打开链接
其实核心记住:
背包是组合问题 填充性质 元素之间没关系
1. 01背包(右->左)
恰好装满(仅初始化[0])否则全为0,有负值(右移) 浮点数(调换w,v),次优(两个dp),K优([i-1][v][1...k] 放/不放都存起来然后归并) ,求w+v最大 (一样)
2. 完全背包 不限量,但依照V还是有限制(左->右)多重背包 都用标记数组方法,coins poj1742。
3. 混合背包 HDU 3535
最多一个(每次比较都是上个阶段放入与当前阶段不放,初始化为上一阶段),最少一个(init设定-1,若当前阶段-1则放入,否则取最优),无所谓(01)
4. 二维费用背包 两个属性,多一层循环。eg: 01背包限制为仅取M个物品,用M外循环,表示N个物品填充使得M个最优。dp[m][j] = dp[m-1][j-v[i]]+w[i];
5. 分组背包 混合背包差不多 hdu 1712 n个组,每个组n个物品,进行选取 至多一个。 hdu 3033 至少一个
![](http://images.cnitblog.com/blog/471463/201307/29152908-089ec33489d849b484e6fd252c958d3f.jpg)
. “物品” 可以是规则 poj1170
关键字:
动态转移方程
自底向上
自上而下
备忘录
分解子问题
学习DP,背包问题似乎是永远绕不过去的。背包问题其实是多阶段状态填充。结合昨晚因为输出看错WA了一晚上的 POJ1384 (完全背包)来谈。
一、0-1背包
问题:有个容量为V大小的背包,有很多不同重量weight[i](i=1..n)不同价值value[i](i=1..n)的物品,每种物品只有一个,想计算一下最多能放多少价值的货物。
每个物体i,对应着两种状态:放入&不放入背包。背包的最优解是在面对每个物体时选择能够最大化背包价值的状态。0-1背包的状态转移方程为
一维数组从右向左迭代就代表从上一次的i-1转化过来。
0-1背包恰好背满
这个问题有时会碰到,(POJ 1384就是如此)与不要求装满背包的区别就是在初始化的时候,对于没有要求必须装满背包的情况下,初始化最大价值都为0,是不存在非法状态的,所有的都是合法状态,因为可以什么都不装,这个解就是0,但是如果要求恰好装满,则必须区别初始化,除了f[0]=0,其他的f[1…v]均设为-∞或者一个比较大的负数来表示该状态是非法的。
这样的初始化能够保证,如果子问题的状态是合法的(恰好装满),那么才能得到合法的状态;如果子问题状态是非法的,则当前问题的状态依然非法,即不存在恰好装满的情况。
二、完全背包
就是一个物品可以用多次,区别使用就是一维数组从左到右迭代即可。因为同一物品可以用多次,同样是对该状态操作。
二进制拆分。将物品容量与背包容量比较化为 1,2,4,8......n-sigma(w)。这样的0-1背包形式。
逆向思维,将容量放在外层循环。一个物品可以用多次,状态因容量而变化。
二维费用(HDU 3496 ,POJ 2576)
二维费用的背包问题
有N件物品,每件物品,具有两种不同的费用,最大费用分别为V1和V2;
选择这件物品必须同时付出这两种代价;
第i件物品所需的两种代价分别为c1[i]和 c2[i],物品的价值为w[i]。
f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i]}
二维费用就是要求满足两种需求不再单单是w了。 3496 这道题刚开始没看出来N个中选M个也是一个“费用”。
这道题我想的是用替换方法去做,即先放入M个,完后每次再用后面的一个去替换,遍历M个中最优替换方案,然后替换之。
MyCode:
WA的原因:
假设 M+1 的时候 用 M + 1 替换了 1 , 改变的只有dp[1]这个数组
然后 M + 2 的时候用 M + 2 替换了 2, 只会 改变 dp[2] 这个数组
你没办法保证同时 用 M + 1 替换 1, M + 2 替换 2
正确的方法则是将m-1个物品看作
AC:
HDU 3033
他人总结:点击打开链接 背包九讲:点击打开链接
其实核心记住:
背包是组合问题 填充性质 元素之间没关系
1. 01背包(右->左)
恰好装满(仅初始化[0])否则全为0,有负值(右移) 浮点数(调换w,v),次优(两个dp),K优([i-1][v][1...k] 放/不放都存起来然后归并) ,求w+v最大 (一样)
2. 完全背包 不限量,但依照V还是有限制(左->右)多重背包 都用标记数组方法,coins poj1742。
3. 混合背包 HDU 3535
最多一个(每次比较都是上个阶段放入与当前阶段不放,初始化为上一阶段),最少一个(init设定-1,若当前阶段-1则放入,否则取最优),无所谓(01)
4. 二维费用背包 两个属性,多一层循环。eg: 01背包限制为仅取M个物品,用M外循环,表示N个物品填充使得M个最优。dp[m][j] = dp[m-1][j-v[i]]+w[i];
5. 分组背包 混合背包差不多 hdu 1712 n个组,每个组n个物品,进行选取 至多一个。 hdu 3033 至少一个
![](http://images.cnitblog.com/blog/471463/201307/29152908-089ec33489d849b484e6fd252c958d3f.jpg)
. “物品” 可以是规则 poj1170
关键字:
动态转移方程
自底向上
自上而下
备忘录
分解子问题
学习DP,背包问题似乎是永远绕不过去的。背包问题其实是多阶段状态填充。结合昨晚因为输出看错WA了一晚上的 POJ1384 (完全背包)来谈。
一、0-1背包
问题:有个容量为V大小的背包,有很多不同重量weight[i](i=1..n)不同价值value[i](i=1..n)的物品,每种物品只有一个,想计算一下最多能放多少价值的货物。
每个物体i,对应着两种状态:放入&不放入背包。背包的最优解是在面对每个物体时选择能够最大化背包价值的状态。0-1背包的状态转移方程为
0-1背包恰好背满
这个问题有时会碰到,(POJ 1384就是如此)与不要求装满背包的区别就是在初始化的时候,对于没有要求必须装满背包的情况下,初始化最大价值都为0,是不存在非法状态的,所有的都是合法状态,因为可以什么都不装,这个解就是0,但是如果要求恰好装满,则必须区别初始化,除了f[0]=0,其他的f[1…v]均设为-∞或者一个比较大的负数来表示该状态是非法的。
这样的初始化能够保证,如果子问题的状态是合法的(恰好装满),那么才能得到合法的状态;如果子问题状态是非法的,则当前问题的状态依然非法,即不存在恰好装满的情况。
二、完全背包
就是一个物品可以用多次,区别使用就是一维数组从左到右迭代即可。因为同一物品可以用多次,同样是对该状态操作。
二进制拆分。将物品容量与背包容量比较化为 1,2,4,8......n-sigma(w)。这样的0-1背包形式。
逆向思维,将容量放在外层循环。一个物品可以用多次,状态因容量而变化。
二维费用(HDU 3496 ,POJ 2576)
二维费用的背包问题
有N件物品,每件物品,具有两种不同的费用,最大费用分别为V1和V2;
选择这件物品必须同时付出这两种代价;
第i件物品所需的两种代价分别为c1[i]和 c2[i],物品的价值为w[i]。
f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i]}
二维费用就是要求满足两种需求不再单单是w了。 3496 这道题刚开始没看出来N个中选M个也是一个“费用”。
这道题我想的是用替换方法去做,即先放入M个,完后每次再用后面的一个去替换,遍历M个中最优替换方案,然后替换之。
MyCode:
#include<math.h> #include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; #define inf 10000000 #define maxn 10+100 typedef __int64 ll; #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) int N,M,K,L; int dp[maxn][110]; struct P { int v, w; }p[maxn]; bool cmp(const struct P &a,const struct P &b) {return a.w<b.w;} int main() { int i,j,T; scanf("%d",&T); while(T--) { scanf("%d%d%d",&N,&M,&L); for(i=1;i<=N;i++) scanf("%d%d",&p[i].w,&p[i].v); sort(p+1,p+1+N,cmp); int tag;int ans=0; memset(dp[1],0,sizeof(dp[1])); for(i=1;i<=M;i++) { for(j=L,tag=1;j-p[i].w>=0;j--) dp[1][j]=dp[1][j-p[i].w]+p[i].v,ans=max(ans,dp[1][j]),tag=0; if(tag)break; } if(tag){puts("0");continue;} for(i=2;i<=M;i++) memcpy(dp[i],dp[1],sizeof(dp[1])); for(i=M+1;i<=N;i++) { for(j=L;j-p[i].w>=0;j--) { for(int k=1;k<=M;k++)//同一维 { if(p[k].w>j)continue; if(j-p[k].w+p[i].w>L)continue; if( dp[k][j-p[k].w+p[i].w]< dp[k][j-p[k].w]+p[i].v) dp[k][j-p[k].w+p[i].w]= dp[k][j-p[k].w]+p[i].v; ans=max(ans,dp[k][j-p[k].w+p[i].w]); } } } printf("%d\n",ans); } return 0; }
WA的原因:
假设 M+1 的时候 用 M + 1 替换了 1 , 改变的只有dp[1]这个数组
然后 M + 2 的时候用 M + 2 替换了 2, 只会 改变 dp[2] 这个数组
你没办法保证同时 用 M + 1 替换 1, M + 2 替换 2
正确的方法则是将m-1个物品看作
AC:
#include<iostream> #include<cstdio> using namespace std; #define INF -999999999 #define N 200005 #define pos 100000 #define max(a,b) (a)>(b)?(a):(b) int dp ; int minn,maxn; int main() { int n,i,j,s,t,ans; while(scanf("%d",&n)!=EOF) { for(i=0;i<N;i++) dp[i]=INF; dp[pos]=0; minn=maxn=pos; for(i=0;i<n;i++) { scanf("%d%d",&s,&t); if(s>0) { for(j=maxn;j>=minn;j--) dp[j+s]=max(dp[j+s],dp[j]+t); maxn=maxn+s; } else { for(j=minn;j<=maxn;j++) dp[j+s]=max(dp[j+s],dp[j]+t); minn=minn+s; } } ans=0; for(i=pos;i<=maxn;i++) if(dp[i]>=0) ans=max(ans,i-pos+dp[i]); printf("%d\n",ans); } return 0; }
HDU 3033
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define maxn 10500 int dp[11][maxn]; int N,M,K; struct P{ int w,v,id; friend bool operator < (P x,P y){ return x.id < y.id; } }p[110]; int main(){ while(~scanf("%d%d%d",&N,&M,&K)){ for(int i = 0;i < N;i++) scanf("%d%d%d",&p[i].id,&p[i].w,&p[i].v); sort(p,p+N); int cnt = 0; memset(dp,-1,sizeof(dp)); for(int i = 0;i <= M;i++) dp[0][i] = 0; for(int i = 0;i < N;i++){ if(p[i].id != cnt) cnt++; for(int j = M;j >= p[i].w;j--){ if(dp[cnt][j-p[i].w] !=-1) dp[cnt][j] = max(dp[cnt][j],dp[cnt][j-p[i].w] + p[i].v); if(dp[cnt-1][j-p[i].w] !=-1) dp[cnt][j] = max(dp[cnt][j],dp[cnt-1][j-p[i].w] + p[i].v); }///上面两句如果换位置,则w为0的元素会加两次 } if(dp[K][M] == -1) printf("Impossible\n"); else printf("%d\n",dp[K][M]); } ///之前没有每次都memset dp -1, 以为在做的过程中cnt++ 那里会改,但是最后判断Impossible就出问题了, return 0; } /** 5 10000 3 1 4 6 2 5 7 3 4 99 1 55 77 2 44 66 3 10000 2 1 2 3 1 3 4 1 5 6 5 10000 3 1 1000 3 1 1000 4 2 100000 4 3 1000 3 3 1000 4 **/
相关文章推荐
- 背包问题总结第六讲——二维费用的背包问题
- DP背包问题小结(01背包,完全背包,需恰好装满或不需,一维DP、二维DP)
- 背包问题总结第三讲——完全背包问题
- P05: 二维费用的背包问题
- 【算法复习三】算法设计技巧与优化----各种背包问题总结
- 【算法复习二】0-1背包问题总结
- 背包问题总结第一讲——可拆分背包
- 背包问题总结(0-1背包+完全背包+多重背包)
- 背包问题总结(0-1背包+完全背包+多重背包)
- tyvj 1214 硬币问题 完全背包 恰好 最大/最少
- 01背包问题 hnust_xiehonghao的总结
- 背包问题总结第七讲——分组的背包问题
- 0-1背包问题;动态规划;时间复杂度O(n方);给出最大价值与解得情况;内有动态规划思路总结;
- 二维费用的背包问题
- 0-1背包问题小总结(hdu 2062)
- 背包问题总结第五讲——混合三种背包问题
- 背包问题总结第十讲——背包问题问法的变化
- TOJ 3596. Watch The Movie【基础的二维费用背包问题】
- P05: 二维费用的背包问题
- 【转】背包问题总结(0-1背包+完全背包+多重背包)