牛逼的是运用二进制的思想来这样拆分,就可以简化for循环的复杂度,又可以达到for单个循环的效果。
所以直接记模板:
for(int i=1;i<=种类数;i++)
{
int k=1; //对于每一种,k准备取1 2 4 8...
int temp=m[i]; //m[i]为第i种的数量
for(k; k<=temp ; k*=2)
{
value[++num]=k*v[i]; //v[i]为第i种的每个的价值
temp-=k;
}
if(temp>0)
value[++num]=temp*v[i];
}
这样就可以得到所有种类任意价值数的组合。
多重背包其实就是这样的,先把value数组给弄好了,然后再把value数组的元素每一个都看成一个单独的背包,对这些背包来做01背包的操作即可。
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6;
long long value[maxn];
const int maxnn=1e6;
long long dp[maxnn];
int main()
{
int m[7];
for(int i=1;i<=6;i++)
cin>>m[i]; //输入每种娃娃有多少个
int num=-1;
for(int i=1;i<=6;i++)
{
int k=1;
int temp=m[i];
for(k; k<=temp ; k*=2)
{
value[++num]=k*i; //都拆分
temp-=k;
}
if(temp>0)
value[++num]=temp*i;
}
long long allsum=0;
for(int i=1;i<=6;i++)
{
allsum+=i*m[i];
}
if (allsum%2!=0)
{
cout<<"Can't be divided.";
return 0;
}
long long half=allsum/2;
for(int i=0;i<=num;i++)
{
for(long long j=half;j>=value[i];j--)
{
dp[j]=max(dp[j],dp[j-value[i]]+value[i]);
}
}
if(dp[half]==half)
cout<<"Can be divided.";
else
cout<<"Can't be divided.";
return 0;
}
代码中加粗部分是第二个我新领会到的知识点(用我的话来阐述):
其实当一个dp数组,下标和它的数值代表的是同一个东西A时,结合max函数来做dp,最后,去判断dp[num]==num其实也就是在判断:当我想要num个A时,我究竟能不能得到num个A。若相等,则能。
另外,此题之前有想过拆分之后用“二进制枚举”的方法来组合,无法AC,发现:二进制枚举存在一个是数位不够(i弄成long long 也不行),一个是时间复杂度过高(num * 2^num)的问题。