Problem
Background
设有 1g, 2g, 3g, 5g, 10g, 20g 的砝码各若干枚(其总重 ≤ 100, 000),要求:计算用这些砝码能称出的
不同重量的个数,但不包括一个砝码也不用的情况。
Input
一行,包括六个正整数 a 1 , a 2 , a 3 , a 4 , a 5 , a 6 ,表示 1g 砝码有 a 1 个,2g 砝码有 a 2 个,……,20g 砝码
有 a 6 个。相邻两个整数之间用单个空格隔开。
Output
以“Total=N”的形式输出,其中 N 为可以称出的不同重量的个数。
Example
weight.in
1 1 0 0 0 0
weight.out
Total=3
Explanation
样例给出的砝码可以称出 1g, 2g, 3g 三种不同的重量。
Scoring
• 对于 20% 的数据,砝码总个数不超过 20。
• 对于 40% 的数据,砝码总个数不超过 800。
Solution
也是一道NOI OpenJudge上的一道题
这题首先是一个背包问题,转移状态可行性。
dp[i][j]表示第i个包,总重为j的可行性
dp[i][j] |= dp[i - 1][j - k * c[i]]
然后采用二进制优化
用一个bitset,第i为表示重量为i的可行性
将每个
ai
拆分
20,21,22…2ki,pi
每次更新bitset即为
A|=A<<ci∗a′i
统计A中1的个数
Code
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define red(i, a, b) for (int i = (a); i <= (b); i--)
#define ll long long
const int N = 110000;
int v[6] = {1, 2, 3, 5, 10, 20};
int sum, ans, m;
int k[10], f[150000], num[150000];
bitset <N> A;
int main() {
rep(i, 0, 5) scanf("%d", &k[i]);
m = 0;
rep(i, 0, 5) {
int base = 1;
while(k[i] >= base) {
num[++m] = base * v[i];
k[i] -= base;
base *= 2;
}
if (k[i] > 0) num[++m] = k[i] * v[i];
}
A[0] = 1;
rep(i, 1, m) A |= (A << num[i]);
printf("Total=%d\n", A.count() - 1);
return 0;
}
这几天的吐槽会专门开一篇的