【洛谷 P2347】[NOIP1996 提高组] 砝码称重 题解(动态规划+01背包+位集合)

99 阅读2分钟

[NOIP1996 提高组] 砝码称重

题目描述

设有 1g1\mathrm{g}2g2\mathrm{g}3g3\mathrm{g}5g5\mathrm{g}10g10\mathrm{g}20g20\mathrm{g} 的砝码各若干枚(其总重 1000 \le 1000),可以表示成多少种重量?

输入格式

输入方式:a1,a2,a3,a4,a5,a6a_1 , a_2 ,a_3 , a_4 , a_5 ,a_6

(表示 1g1\mathrm{g} 砝码有 a1a_1 个,2g2\mathrm{g} 砝码有 a2a_2 个,\dots20g20\mathrm{g} 砝码有 a6a_6 个)

输出格式

输出方式:Total=N

NN 表示用这些砝码能称出的不同重量的个数,但不包括一个砝码也不用的情况)

样例 #1

样例输入 #1

1 1 0 0 0 0

样例输出 #1

Total=3

提示

【题目来源】

NOIP 1996 提高组第四题


思路

首先定义一个位集dp,大小为 NN,其中 N=1000+7N=1000+7,并将其所有位重置为0。位集dp用于存储能够称出的重量,如果某个重量能被称出,那么对应的位就被设置为1。

然后从输入中读取砝码的数量,存储在数组a中。

接下来,将dp[0]设置为1,表示0克的重量可以被称出(即不使用任何砝码)。

之后,进行三层循环。外层循环遍历所有的砝码种类,中层循环遍历每种砝码的数量,内层循环遍历从1000克到0克的所有重量。在内层循环中,如果某个重量k可以被称出(即dp[k]为1),那么重量k+W[i]也可以被称出,其中W[i]是砝码的重量。因此,将dp[k+W[i]]设置为1。

最后,将dp[0]设置为0,表示不包括一个砝码也不用的情况,然后输出能够称出的重量的数量,即dp中值为1的位的数量。

注意

因为是01背包问题,一枚砝码只能使用一次,所以k要从1000到0遍历,否则会重复使用同一枚砝码。


AC代码

#include <algorithm>
#include <bitset>
#include <iostream>
#define AUTHOR "HEX9CF"
using namespace std;
using ll = long long;

const int N = 1e3 + 7;
const int INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;

const int W[6] = {1, 2, 3, 5, 10, 20};

int a[6];
bitset<N> dp;

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	dp.reset();

	for (int i = 0; i < 6; i++) {
		cin >> a[i];
	}

	dp[0] = 1;
	for (int i = 0; i < 6; i++) {
		for (int j = 1; j <= a[i]; j++) {
			for (int k = 1000; k >= 0; k--) {
				dp[k + W[i]] = dp[k];
			}
		}
	}

	// for (int i = 1; i <= 1000; i++) {
	// 	if (dp[i]) {
	// 		cout << i << "\n";
	// 	}
	// }
	dp[0] = 0;
	cout << "Total=" << dp.count() << "\n";
	return 0;
}