原题链接:https://www.luogu.com.cn/problem/P1441
题目大意:现有n个砝码,重量分别为 ,在去掉 m 个砝码后,问最多能称量出多少不同的重量(不包括 0)。
观察数据范围并不是很大,所有选取方案至多,先考虑比较直接的算法。
思路:利用二进制数作为状态数进行枚举:一个n位二进制数第i位是否为1代表选不选用第i个砝码。这个二进制数需要满足条件:有且仅有n-m位是1,表示从n中抛弃了m个砝码。
这里我们引入bitset,可以把它看成是一个数组,但是这个数组的每一位都只有一个比特,值为0或者1,bitset可以方便的进行移位等运算。
下面的程序中:bitset的应用:创建,
表示是否存在一种组合,能够称量质量为
的物体。显然
.
为了方便说明,举具体实例:
创建临时变量,它同样为一个bitset,对于
,
,这时
为0000...010,表示能够称量质量为1的物体。做
,表示
的能够称量的质量范围为加上砝码
之后的新的范围。
之后处理,
,处理完之后
为0000...1100,再做
,此时
为0000...1111,代表bt能够称量质量为3、2、1、0(按题目要求不算)的物体,总共有3种有效质量。
预处理:把所有满足条件的二进制数先用一个数组存储起来。
#include<bits/stdc++.h>
using namespace std;
bitset <10005> bt;
bitset <10005> bp;
int cnt, n, m;
int w[10000], a[105];
void dfs(int depth, int sum, int state)
{
//state作为二进制数表示砝码的选取情况
if(depth == n)
{
if(sum == n - m)w[++cnt] = state;//总共选取了n-m个砝码
return;
}
dfs(depth + 1, sum, state);//抛弃当前砝码
dfs(depth + 1, sum + 1, state + (1 << depth));//选择当前砝码
}
主程序:
int main()
{
cin>>n>>m;
dfs(0,0,0);
for(int i = 0;i < n; i++)cin>>a[i];
int ans = -0x3f3f3f3f;
for(int i = 1;i <= cnt; i++)
{
//枚举每一种选取砝码的情况
int state = w[i];
bt[0] = 1;
for(int j = 0;j < n; j++)
{
if(state & (1 << j))
{
bp = bt << a[j];
bt = bp | bt;
}
}
int t = 0;
//计算当前的选取方案能够称量的质量总数,这里的2000主要是因为题目数据范围
for(int k = 1;k <= 20*100; k++)
{
if(bt.test(k))t++; //bt[k]=1,则代表能称质量为k的物体
}
//bitset清零
bt.reset();
bp.reset();
ans = max(t, ans);
}
cout<<ans;
return 0;
}