[NOIP1996 提高组] 砝码称重
题目描述
设有 1 g 1\mathrm{g} 1g、 2 g 2\mathrm{g} 2g、 3 g 3\mathrm{g} 3g、 5 g 5\mathrm{g} 5g、 10 g 10\mathrm{g} 10g、 20 g 20\mathrm{g} 20g 的砝码各若干枚(其总重 $ \le 1000$),可以表示成多少种重量?
输入格式
输入方式: a 1 , a 2 , a 3 , a 4 , a 5 , a 6 a_1 , a_2 ,a_3 , a_4 , a_5 ,a_6 a1,a2,a3,a4,a5,a6
(表示 1 g 1\mathrm{g} 1g 砝码有 a 1 a_1 a1 个, 2 g 2\mathrm{g} 2g 砝码有 a 2 a_2 a2 个, … \dots …, 20 g 20\mathrm{g} 20g 砝码有 a 6 a_6 a6 个)
输出格式
输出方式:Total=N
( N N N 表示用这些砝码能称出的不同重量的个数,但不包括一个砝码也不用的情况)
样例 #1
样例输入 #1
1 1 0 0 0 0
样例输出 #1
Total=3
提示
【题目来源】
NOIP 1996 提高组第四题
思路
首先定义一个位集dp
,大小为 N N N,其中 N = 1000 + 7 N=1000+7 N=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;
}