背包问题
1 01背包问题
有N件物品和一个容量为V的背包。(每种物品均只有一件)第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
解法:我们用dp[i][j]表示前i件物品重量不超过j的最大价值,则dp方程为
j>w[i]时,dp[i][j]=max{dp[i-1][j],dp[i-1][j-w[i]]+c[i]}
j<=w[i]时,dp[i][j]=dp[i-1][j].
其中dp[i-1][j]表示第i件物品无法放在背包里的价值,dp[i-1][j-w[i]]+c[i]表示第i件物品放入背包里的最大价值,我们选择其中的较大的。
优化:
采用一维数组来优化dp[v],我们必须保证dp[v]及dp[v-w[i]]是前一个状态的值,及前i-1件物品的最大价值,如何保证这个无后效性呢,采用逆序
dp[v]=max{dp[v],dp[v-w[i]]+c[i]},这样计算出来的每一个新的dp[v]都不会影响当前循环的后面的结果,核心代码:
for(i=1;i<=n;i++)
for(v=V;v>=w[i];v--)
dp[v]=max{dp[v],dp[v-w[i]]+c[i]};
2.完全背包
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
解法:
仍然采用刚才的思路,用dp[i][j]表示前i件物品重量不超过j的最大价值,则dp方程为:
dp[i][j]=max{dp[i-k*w[i]]+k*c[i]},0<=k*w[i]<=j
我们对这个也采用一维数组进行优化,令dp[v]表示dp[i][v],由于这个问题中物品是无穷件,所以我们不用考虑前一个状态,只考虑当前状态,即
for(i=1;i<=n;i++)
for(v=w[i];v<=V;v++)
dp[v]=max{dp[v],dp[v-w[i]]+c[i]};
这个和01背包的区别就是因为物品是无限的,我们可以累计的将第i件物品加入到当前状态中。
3.多重背包
有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
dp方程为:dp[i][j]=max{dp[i-1][j-k*w[i]+k*c[i]]},0<=k<=n[i]
对应这个题,由于物品件数有限,我们需要遍历一遍n[i],即将k个第i件物品放入当前背包,选择最大的那个。
for(i=1;i<=n;i++)
for(j=0;j<n[i];j++)//即循环n[i]次,选择最大的dp[v]
for(v=V;v>=w[i];v--)
dp[v]=max{dp[v],dp[v-w[i]]+c[i]};
poj1276 ,多重背包问题,但是由于数比较大超时了,必须经过优化,以后有时间再看吧,下面是我超时的代码:
#include <iostream> #include <stdio.h> using namespace std; const int N=100001; int dp[N+2],weight[N],num[N],total,n; int main() { int i,j,k; while(scanf("%d%d",&total,&n)!=EOF) { for(i=1;i<=n;i++) scanf("%d%d",&num[i],&weight[i]); for(i=0;i<=total;i++) dp[i]=0; if(!total || !n) { printf("%d\n",0); continue; } for(i=1;i<=n;i++) { for(j=0;j<num[i];j++) { for(k=total;k>=weight[i];k--) { if(dp[k]<(dp[k-weight[i]]+weight[i])) dp[k]=dp[k-weight[i]]+weight[i]; } } } printf("%d\n",dp[total]); } return 0; }