一.01背包
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。
状态转移方程:f[i][v]=max{ f[i-1][v] , f[i-1][v-c[i]]+w[i] }
将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为j的背包中”,价值为f[i-1][v];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”,此时能获得的最大价值就是f[i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i]。
#include<bits/stdc++.h>
using namespace std;
int dp[1005][1005];
int weight[1005];
int value[1005];
int main()
{
int n,m;
cin>>m>>n;
memset(dp,0,sizeof(dp));//数组清空,其实同时就把边界给做了清理
for(int i=1; i<=n; i++)
cin>>weight[i]>>value[i];
//从1开始有讲究的因为涉及到dp[i-1][j],从0开始会越界
for(int i=1; i<=n; i++)//判断每个物品能否放进
{
for(int j=0; j<=m; j++)//对每个状态进行判断
//这边两重for都可以倒着写,只是需要处理最边界的情况,滚动数组不一样
{
if(j>=weight[i])//能放进
dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
else dp[i][j]=dp[i-1][j];//不能放进
}
}
cout<<dp[n][m]<<endl;
return 0;
}
空间优化
#include<bits/stdc++.h>
using namespace std;
int dp[1005];//滚动数组的写法,省下空间省不去时间
int weight[1005];
int value[1005];
int main()
{
int n,m;
cin>>m>>n;
memset(dp,0,sizeof(dp));
for(int i=1; i<=n; i++)
cin>>weight[i]>>value[i];
for(int i=1; i<=n; i++)//对每个数判断,可反
{
for(int j=m; j>=weight[i]; j--)//这里这个循环定死,不能反,反了就是完全背包
{
dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);//其实不断在判断最优解,一层一层的
}
}
cout<<dp[m]<<endl;
return 0;
}
二.完全背包
特点:每种物品有无限件
#include<bits/stdc++.h>
using namespace std;
int dp[100005];//m
struct Node{
int a,b;
}node[1005];//n
int main(){
int n;
while(~scanf("%d",&n)){
for(int i=0;i<n;i++){
scanf("%d%d",&node[i].a,&node[i].b);
}
int m;
scanf("%d",&m);
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++){
for(int j=node[i].b;j<=m;j++){//这样就是完全背包
dp[j]=max(dp[j],dp[j-node[i].b]+node[i].a);
}
}
printf("%d\n",dp[m]);
}
return 0;
}
三.多重背包
特点:每种物品有有限件num[i]
可以把物品拆开,把相同的num[i]件物品 看成 价值跟重量相同的num[i]件不同的物品
#include<bits/stdc++.h>
using namespace std;
int dp[1005];
int weight[1005],value[1005],num[1005];
int main()
{
int n,m;
cin>>n>>m;
memset(dp,0,sizeof(dp));
for(int i=1; i<=n; i++)
cin>>weight[i]>>value[i]>>num[i];
for(int i=1; i<=n; i++)//每种物品
for(int k=0; k<num[i]; k++)//其实就是把这类物品展开,调用num[i]次01背包代码
for(int j=m; j>=weight[i]; j--)//正常的01背包代码
dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
cout<<dp[m]<<endl;
return 0;
}