背包问题(好奇怪)
01背包:
有N件物品和一个容量为V的背包。
第i件物品的费用(即体积,下同)是w[i],价值是c[i]。
求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
解题基本思路:
这是最基础的背包问题
每种物品仅有一件,可以选择放或不放。
用子问题定义状态:即f[i][v]表示前i件物品(部分或全部)恰放入一个容量为v的背包可以获得的最大价值。
其状态转移方程便是:f[i][v]=max{f[i-1][v],f[i-1][v-w[i]]+c[i]}
这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。
将前i件物品放入容量为v的背包中”这个子问题,
若只考虑第i件物品的策略(放或不放),
那么就可以转化为一个只牵扯前i-1件物品的问题。如果不放第i件物品,
那么问题就转化为“前i-1件物品放入容量为v的背包中”;如果放第i件物品,
那么问题就转化为“前i-1件物品放入剩下的容量为v-w[i]的背包中”,
此时能获得的最大价值就是f [i-1][v-w[i]]再加上通过放入第i件物品获得的价值c[i]。
注意f[i][v]有意义当且仅当存在一个前i件物品的子集,其费用总和为v。
所以按照这个方程递推完毕后,最终的答案并不一定是f[N][V],而是f[N][0..V]的最大值。
如果将状态的定义中的“恰”字去掉,在转移方程中就要再加入一项f[i-1][v],这样就可以保证f[N][V]就是最后的答案。
但是若将所有f[i][j]的初始值都赋为0,你会发现f[n][v]也会是最后的答案。为什么呢?因为这样你默认了最开始f[i][j]是有意义的,
只是价值为0,就看作是无物品放的背包价值都为0,所以对最终价值无影响,这样初始化后的状态表示就可以把“恰”字去掉。
在背包问题中,有两种解决办法,一种是二维数组,一种是一维数组。
例题:
洛谷P1048
一般情况,,01背包问题的解决方法:
//二维数组: for(int i=1;i<=n;i++) for(int j=v;j>=0;j--)//v指的是背包容量 f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+c[i]//w[i]是第i件物品的价值 cout<<f[n][v];
相较于二维数组来说,一维数组会相对较快一些,所以掌控一维数组的01背包问题,是十分重要的。
//一维数组 for(i=1;i<=n;i++) for(j=v;j>=0;j--) f[j]=max(f[j-h[i]]+w[i], f[j]); cout<<f[v]<<endl;
此题题解:
#include<bits/stdc++.h> using namespace std; int t,m,i,j; int h[105],w[105],f[50000]; int max(int x,int y)//自定义的比较大小函数 {//其实在algorithm函数库中有自带max和min比较函数 return x>y? x:y; } int main() { cin>>t>>m; for(i=1;i<=m;i++) cin>>h[i]>>w[i]; for(i=1;i<=m;i++) for(j=t;j>=h[i];j--) { f[j]=max(f[j-h[i]]+w[i], f[j]);//动态规划方程 } cout<<f[t]<<endl; return 0; }
嗯呐嗯呐,01背包就是这个样子!!!