背包模型
1.0 1.0 1.0 01 01 01背包问题
每一个物品只能取 1 1 1 次 , C + + ,C++ ,C++ 模板 : : :
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,Arr[110];
int Dp[100010];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&Arr[i]);
Dp[0]=1;
for(int i=1;i<=n;i++)
for(int j=m;j>=Arr[i];j--)
Dp[j]+=Dp[j-Arr[i]];
printf("%d\n",Dp[m]);//m价值的排列个数
return 0;
}
2.0 2.0 2.0 完全背包问题
每一个物品能取多次 , C + + ,C++ ,C++ 模板 : : :
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,Arr[110];
int Dp[100010];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&Arr[i]);
Dp[0]=1;
for(int i=1;i<=n;i++)
for(int j=Arr[i];j<=m;j++)
Dp[j]+=Dp[j-Arr[i]];
printf("%d\n",Dp[m]);//m价值的排列个数
return 0;
}
3.0 3.0 3.0 多重背包问题
有 n n n 种物品 , , , 每个物体有 C n t i Cnt_i Cnti 个 , , , 每一个的价值是 A r r i , Arr_i, Arri, 每一个的体积是 V o l i , Vol_i, Voli, 问容量为 v v v 的背包能装下的价值总和最大的是多少 . . .
3.0.1 3.0.1 3.0.1 直接拆分法
把每个物品拆分成 C n t i Cnt_i Cnti 个物品 , , , 然后再跑一遍带价值的一遍背包 . . .
3.0.2 3.0.2 3.0.2 二进制拆分法
把每个物品分成多个不同的物品 , , , 每个物品的价值 , , , 体积分别为原来的 2 0 , 2 1 , 2 2 ⋯ 2 p 2^0,2^1,2^2\cdots 2^p 20,21,22⋯2p 倍 ( 2 0 + 2 1 + 2 2 ⋯ + 2 p < C n t i ) , (2^0+2^1+2^2\cdots+2^p<Cnt_i), (20+21+22⋯+2p<Cnti), 余下来的物品在组成一个新物品 , , , 再跑一次带价值的一遍背包即可 . . .
3.0.3 3.0.3 3.0.3 单调队列优化
请见于单调队列优化处.
3.0.4 3.0.4 3.0.4 Acwing281
此题为多重背包问题的特殊情况 , , , 询问可行性 . . .
我们考虑贪心 , , , 动态规划最小消耗 . . .
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,Arr[101],Cnt[101],Dp[2][100011];
int main()
{
while(~scanf("%d%d",&n,&m)&&(n||m))
{
for(int i=1;i<=n;i++)
scanf("%d",&Arr[i]);
for(int i=1;i<=n;i++)
scanf("%d",&Cnt[i]);
memset(Dp,-1,sizeof(Dp));
Dp[0&1][0]=0;
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
if(Dp[(i-1)&1][j]>=0)
Dp[i&1][j]=Cnt[i];
else if(j-Arr[i]>=0&&Dp[i&1][j-Arr[i]]>=1)
Dp[i&1][j]=Dp[i&1][j-Arr[i]]-1;
int ans=0;
for(int j=1;j<=m;j++)
if(Dp[n&1][j]>=0)ans++;
printf("%d\n",ans);
}
return 0;
}
/*
若f[j]能被拼出
1.前i-1种硬币就能拼成面值j,即在第i阶段开始前,f[j]已经成为true。
2.使用了第i种硬币,即在第i阶段的递推过程中,发现f[j-a[i]]为true,从而使用一个i硬币,使f[j]变为true。
于是可以考虑一种贪心策略:设used[j]表示在第i阶段下将f[j]变为true至少需要使用多少枚第i种硬币。
为了满足我们的贪心策略,我们应该尽量多的使用第1种情况,也就是说在f[j-a[i]]为true时,若f[j]已经为true,就不进行DP转移,并令used[j]=0(我们并没有使用第i种硬币)仅当f[j-a[i]]为true而f[j]为false且已经使用的i硬币used[j-a[i]]<c[i]时,我们使用一个i硬币使f[j]变为true,used[j]=used[j-a[i]]+1;
作者:AsadaShino
链接:https://www.acwing.com/solution/content/11075/
*/
4.0 4.0 4.0 分组背包问题
有 n n n组物品 , , , 每一组至多选择一个物品 ( ( (也可以不选 ) , ), ), 每个物品的体积为 V o l i Vol_i Voli和价值 A r r i , Arr_i, Arri,问容量为 v v v的背包能装下的物品价值总和最大值 . . .
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,v,Dp[1100];
int Cnt[1100],Arr[1100][1100],Vol[1100][1100];
int main()
{
memset(Dp,0xcf,sizeof(Dp));Dp[0]=0;
scanf("%d%d",&n,&v);
for(int i=1;i<=n;i++)
{
scanf("%d",&Cnt[i]);
for(int j=1;j<=Cnt[i];j++)
scanf("%d",&Vol[i][j]);
for(int j=1;j<=Cnt[i];j++)
scanf("%d",&Arr[i][j]);
}
for(int i=1;i<=n;i++)//阶段
for(int j=v;j>=0;j--)//状态
for(int k=1;k<=Cnt[i];k++)//决策
if(j>=Vol[i][k])
Dp[j]=max(Dp[j],Dp[j-Vol[i][k]]+Arr[i][k]);
printf("%d\n",Dp[v]);
return 0;
}