砝码称重【第十二届蓝桥杯】【省赛】【B组】
Description
你有一架天平和N个砝码,这N个砝码重量依次是W1、W2,…WN
请你计算一共可以称出多少种不同的重量?
注意砝码可以放在天平两边。
Input
输入的第一行包含一个整数N。(1 ≤ N ≤ 100)
第二行包含N个整数:W1, W2, W3, …WN;
(N个砝码总重不超过100000)
Output
输出一个整数代表答案。
Sample Input
3
1 4 6
Sample Output
10
Pasing
第一反应DFS,把每一种砝码看作3个选择,放在左边、放在右边、不放上去。由此可以得到三种状态:-w[i], +w[i], +0,然后进行深搜,递归每一种情况,然后把最终结果放入set集合中(自动剔重),最后erase(0)即可。
但是这道题直接DFS会TLE,据说可以记忆化搜索,但是我没有尝试。
所以我们再来重新看带上面的思路。本题与洛谷P1877 音量调节类似,其实每一个砝码就是拿或不拿的区别,这时候就很容易想到背包了,背包需要判断拿或者不拿,我们这里判断的是放左边、放右边、不拿。即第拿到第 i 个砝码时要放在第 i - 1 个时的左边还是右边:放在左边就加减掉,放在右边就加上,不拿就保持原来的状态。
也就是说我们需要准备的是拿到第i个砝码时的的状态。我们保存拿到第 i 个砝码时可达的所有重量,做下标记,然后根据这些标记去放第i + 1 个砝码。
Code
#include <iostream>
#include <cmath>
using namespace std;
int main() {
int n;
int w[105];
int dp[105][100005];
int sum = 0;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> w[i];
sum += w[i];
}
dp[1][w[1]] = 1;//只有一个砝码时,可达的重量只有w[1]
for (int i = 2; i <= n; i++) {
dp[i][w[i]] = 1;//只放这一个砝码
for (int j = 1; j <= sum; j++) {
if (dp[i - 1][j] == 1) {//只有前 i - 1 个砝码时可以达到j重量
dp[i][j] = 1;//不放第 i 个砝码,j重量也是可达的。
int temp1 = abs(j - w[i]);//放左边
int temp2 = abs(j + w[i]);//放右边
if (temp1 > 0 && temp1 <= sum) {//特判一下防止越界
dp[i][temp1] = 1;
}
if (temp2 > 0 && temp2 <= sum) {
dp[i][temp2] = 1;
}
}
}
}
int cnt = 0;
for (int i = 1; i <= sum; i++) {
if (dp[n][i] == 1) {//有所有的砝码时可达的重量都被标记为1
cnt++;
}
}
cout << cnt << endl;
return 0;
}