洛谷 - P1441 砝码称重 (状态压缩dp,bitset)

砝码称重

题目描述

现有 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 m1

对于 50 % 50\% 50% 的数据, n ≤ 10 n\leq 10 n10

对于 100 % 100\% 100% 的数据, n ≤ 20 n\leq 20 n20 m ≤ 4 m\leq 4 m4 m < n m < n m<n a i ≤ 100 a_i\leq 100 ai100


这道题给出了一种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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值