砝码称重
题目描述
现有 n n n 个砝码,重量分别为 a i a_i ai,在去掉 m m m 个砝码后,问最多能称量出多少不同的重量(不包括 0 0 0)。
请注意,砝码只能放在其中一边。
输入格式
第 1 1 1 行为有两个整数 n n n 和 m m m,用空格分隔。
第 2 2 2 行有 n n n 个正整数 a 1 , a 2 , a 3 , … , a n a_1, a_2, a_3,\ldots , a_n a1,a2,a3,…,an,表示每个砝码的重量。
输出格式
仅包括 1 1 1 个整数,为最多能称量出的重量数量。
样例 #1
样例输入 #1
3 1
1 2 2
样例输出 #1
3
提示
【样例说明】
在去掉一个重量为 2 2 2 的砝码后,能称量出 1 , 2 , 3 1, 2, 3 1,2,3 共 3 3 3 种重量。
【数据规模】
对于 20 % 20\% 20% 的数据, m = 0 m=0 m=0。
对于 50 % 50\% 50% 的数据, m ≤ 1 m\leq 1 m≤1。
对于 50 % 50\% 50% 的数据, n ≤ 10 n\leq 10 n≤10。
对于 100 % 100\% 100% 的数据, n ≤ 20 n\leq 20 n≤20, m ≤ 4 m\leq 4 m≤4, m < n m < n m<n, a i ≤ 100 a_i\leq 100 ai≤100。
这道题给出了一种bitset
的使用思想以及状压dp的一种思考方法。
这道题中枚举的状态每一个数位代表的是某一个砝码选还是不选,这里使用 1 1 1 代表选上, 0 0 0 代表不选。
之后使用bitset
来表示能够表示出来的重量的数量,假如
x
x
x 数位上为1,那么就代表x重量可以被表示。
#include<iostream>
#include<bitset>
#include<vector>
using namespace std;
const int N = 22;
const int M = 1 << 20;
int a[N];
vector<int>st;
int n, m;
int count(int st) {
int res = 0;
for (int i = 0; i < n; i++) {
res += st >> i & 1;
}
return res;
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++)cin >> a[i];
for (int i = 0; i < 1 << n; i++) {
if (count(i) == n - m) { //如果去掉的个数合法
st.push_back(i); //就压入这个状态
}
}
int res = 0;
for (auto t : st) {
bitset<2010>b;
//b的每一位代表能不能表示出相应的重量
//假如b的第x位是1,那就代表重量x可以被代表出来
b[0] = 1;
for (int j = 0; j < n; j++) {
if (t >> j & 1) {
/*或运算的作用
* 我们用b<<a[j],就让b上的每一位1都左移了a[j]位
* 这时候假如b的第一个1在第x位,那么就会被左移到x+a[j]处
* 这样代表着b能够表示出来的重量又多了一些
* 这时候我们用b或,就能够得到之前能够表示的重量和当前能表示的重量的并集
*/
b = b | b << a[j];
}
}
res = max(res, (int)b.count());//这里直接传b.count(),因为1的个数就代表了能够表示的重量的数量
}
cout << res - 1;//根据题目要求去掉重量为0的情况
return 0;
}