思路:将多重背包转化为01背包,因为数据比较小,所以不用二进制优化。
01背包解题:
1.定义数组,f(k)表示前 i 个砝码,重量的组合是否可以为 k ,
2.状态转移,看成 01 背包,如果f(i)可以表示,那么f(i+a[i])也可以表示,所以为f(i+a[i])=f(i);
3.初始化:根据定义来初始化f(0)=1。
4.递推,模仿01背包递推:
代码:
#include <bits/stdc++.h> #define int long long //(有超时风险) #define PII pair<int,int> #define endl '\n' #define LL __int128 using namespace std; const int N=2e5+10,M=1e3+100,mod=998244353,INF=0x3f3f3f3f; int w[]={1,2,3,5,10,20}; int f[M]; int a[M]; signed main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); int n=0; int sum=0; for(int i=0;i<6;i++) { int cnt;cin>>cnt; for(int j=0;j<cnt;j++) { a[++n]=w[i]; sum+=w[i]; } } f[0]=1; for(int i=1;i<=n;i++) for(int j=sum;j>=0;j--) { f[j+a[i]]=f[j]; } int ans=0; for(int i=1;i<=sum;i++) if(f[i])ans++; cout<<"Total="<<ans<<endl; return 0; }
bitset用法:
//f.set()把所有位置设置为1,f.set(0)把第pos==0位设置为1 //string s=t.to_string(); 转化为字符串; //int t=f.test(0); 返回第pos==0位上的值, //f.flip();flip()函数反置bitset中所有的位,即将1设为0,0设为1。 // 如果指定pos,那么只有pos上的位被反置。 //cout<<f.any()<<endl; 检查bitset上是否存在某一位为1 ; // cout<<f.to_ulong()<<endl; 把二进制的f转化为长整形 //f.count()返回f中1的数量;
bitset,二进制优化:
因为总和比较小,所以我们可以优化为与总和有关,因为bitset的每一位都是用bool表示,所以可以用bitset的每一位是否为1,来表示总和是否可以为bitset的位数,例如:1000表示可以取总和为4。#include <bits/stdc++.h> #define int long long //(有超时风险) #define PII pair<int,int> #define endl '\n' #define LL __int128 using namespace std; const int N=2e5+10,M=1e3+10,mod=998244353,INF=0x3f3f3f3f; int w[]={1,2,3,5,10,20}; signed main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); bitset<1020>f; //初始化 f[0]=1; for(int i=0;i<6;i++) { int cnt;cin>>cnt; // | 操作。例子,当w[i]==2时,f为00010时,00010|=01000 ->01010;表示4和2两种重量和都可以取。 for(int j=0;j<cnt;j++) f|=f<<w[i]; } //第0位不算 cout<<"Total="<<f.count()-1<<endl; return 0; }