莫名其妙的神奇背包

今天要谈论的是一堆奇形怪状、莫名其妙、精灵可爱的背包。

416.采药

最基础的01背包模板题,随随便便DP即可。
for (int i=1;i<=n;i++)
  for (int j=m;j>=a[i];j--)
   f[j]=max(f[j],f[j-a[i]]+b[i]);

417.集合求和

01背包求方案数。
for (int i=1;i<=m;i++)//从1-m枚举使用的数(m代表题目中的n)
  for (int j=n;j>=i;j--)//从n的累加和到i枚举累加和(n代表1-m的累加和)
    f[j]=f[j]+f[j-i];//f[j]的方案数为本身方案数+减去i的方案数

418.完全背包

最基础的完全背包模板题,随随便便DP即可。
for (int i=1;i<=n;i++)
  for (int j=a[i];j<=m;j++)
    f[j]=max(f[j],f[j-a[i]]+b[i]);

419.质数和分解

完全背包求方案数
这里要做一个质数的预处理
for (int i=1;i<=p;i++)//p为1-n质数的个数
   for (int j=a[i];j<=n;j++)//a[i]为一个质数
      f[j]=f[j]+f[j-a[i]];//f[j]的方案数为本身方案数+减去a[i]的方案数

420.逃亡的准备

这这这是一道多重背包题。我们可以使用神奇的二进制法来优化这个算法。

比如有一个数字叫做66。
则可以将其分解为1+2+4+8+16+32+3
通过这些神奇的数字就可以拼出1-n的所有数字。
核心代码
while (k<a1)
  {
	 	    
	for (int j=m;j>=k*b[i];j--)
	f[j]=max(f[j],f[j-k*b[i]]+k*c[i]);
	a1-=k;
	k*=2;
  }
for (int j=m;j>=a1*b[i];j--)
  {
        f[j]=max(f[j],f[j-a1*b[i]]+a1*c[i]);
  }

关于该题,如果数量*体积已经超过背包的容积,则可直接当成完全背包来做。

for (int j=b[i];j<=m;j++)
  f[j]=max(f[j],f[j-b[i]]+c[i]);
就省了一堆莫名其妙的事了。

422.竞赛真理

每个物品有两种“体积”和两种“价值”,做两遍01背包即可。
for (int i=1;i<=n;i++)
  for (int j=m;j>=min(b[i],b1[i]);j--)//min(b[i],b1[i])可以保证每次循环都有事干
   {
	if (j>=b[i])
	 f[j]=max(f[j],f[j-b[i]]+a[i]);//如果大于第一种体积,则可以选择这种
	if (j>=b1[i])
         f[j]=max(f[j],f[j-b1[i]]+a1[i]);//如果大于第二种体积,则可以选择这种
   }

423.金明的预算方案

带有依赖性的背包,分为主件附件,须选择对应主件才可选择其附件。
题目中说明尽量选择价格*重要度值高的,也就是说 价格*重要度是该物品的价值
for (int i=1;i<=n;i++)
	 if (c[i]==0)//是主件才进入循环
	  {
	  	for (int j=1;j<=a[i];j++)
	   	 h[j]=0;//清空辅助数组h
	  	for (int j=a[i];j<=m;j++)
	  	  h[j]=f[j-a[i]]+b[i];//给辅助数组赋值(h[j]表示剩下钱为j时最大价值)
	  	for (int j=1;j<=n;j++)
	  	 if (c[j]==i)//如果找到了以i为主件的一件物品
	  	  {
	  	  	for (int k=m;k>=a[i]+a[j];k--)
	  	  	  h[k]=max(h[k-a[j]]+b[j],h[k]);//做一遍01背包
	  	  }
	  	for(int j=a[i];j<=m;j++)
	  	  if (h[j]>f[j])f[j]=h[j];//取大值
	  }



猜你喜欢

转载自blog.csdn.net/fsl123fsl/article/details/78488438