一,题目大意
现有 n 个砝码,重量分别为 a[ i ]在去掉 m 个砝码后,问最多能称量出多少不同的重量(不包括 0)。
请注意,砝码只能放在其中一边。(指不能使用减法)
二、思路
1、算法
本题推荐使用状态压缩(不代表其他不行),因为砝码取或者不取的情况较多,所以我们可以用一个二进制序列来表示砝码的使用情况。
例如 :一个长度为 4 的序列,我们可以用 1001 来表示第一个没有了,第二,三个还留着,第四个也没有了。那么,本来一个很长的序列就可以转换成一个数字了——1001 = 9 。
2、定义
我们将 1 设为拿走了,0 设为没拿走。之后枚举所有的状态,如果正好拿走的个数为 n − m, 我们再去判断答案,也就是把没拿走的去枚举状态。之后判断每一个是否为 1 。
3、科技(好用的东西)
可以使用bitset 进行二进制计算。bitset 的速度比正常数组快 64 倍。
(1)定义:
bitset可以说是一个多位二进制数,每八位占用一个字节,因为支持基本的位运算,所以可用于状态压缩,n位bitset执行一次位运算的时间复杂度可视为n/32.
(2)一些常见的函数:
s.set()把s所有位变为1;
s.set(k,v)把s的第k位改为v,即s[k]=v;
s.reset()把s的所有位变为0.
s.reset(k)把s的第k位改为0,即s[k]=0;
s.flip()把s所有位取反.即s=~s;
s.flip(k)把s的第k位取反,即s[k]^=1;
若s所有位都为0,则s.any()返回false,s.none()返回true;
若s至少有一位为1,则s.any()返回true,s.none()返回false;
s.count() 返回二进制串中有多少个1 (实测好用)
s[k] :表示s的第k位,即可取值也可赋值,编号从0开始;
三、代(Damn)码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,ans,a[50005],w[50000005];
bitset<2010> s;
int lowbit(int x){
return (x&-x);
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=(1<<n);i++){
w[i]=w[i-lowbit(i)]+1;//计算有多少个 1(选了多少数)
if(w[i] == n - m) {
s.reset(); //清空
s[0] = 1; //初始化,注意 0 不能算作砝码拼出来的
for(int j = 1; j <= n; j ++){
if(i & (1 << (j - 1))) { //第 j 个砝码被选了
s |= s << a[j];
}
}
ans = max(ans, (int)s.count() - 1);
}
}
cout<<ans;
}