题目
提交记录
题目分析
分析题目给出的条件:
1<=N<=100,总重不超过100000(即1e5)
如果动态规划做,时间复杂度为1e7,是肯定没问题的。
如果暴力做,复杂度过大,只能得部分的分。
所以,这题很明显是需要动态规划做的,我采用的是通过 f[i][j] 来判断i号砝码能称量出那些重量,砝码号从0~n-1,我们可以从 f[0][ ] 推到 f[n-1][ ],从而得知,在砝码数量为n-1时,能称量出哪些重量。
i表示砝码编号,j表示重量,f[i][j] 表示0~i号砝码都能使用时能否称量出j重量,若能够称量出,则 f[i][j] = 1,否则为0
那么如何从 f[i-1][ ] 推到 f[i][ ] 呢?
我们知道,当只有一个砝码的时候,只能称量出1种重量,所以 f[0][ ] 只有 f[0][w[0]] 这一种状态(w[0]即0号砝码的重量),而 f[1][ ] 多了1个砝码可以使用,即1号砝码,所以可以在 f[0][w[0]] = 1 的基础上得到如下的新状态(f[1][w[1]] = 1在初始化时就可以得到)
新状态 | 状态产生原因 |
f[1][w[0]] = 1 | 不使用1号砝码 |
f[1][w[0] + w[1]] = 1 | 增加1号砝码后,放在天平同侧 |
f[1][abs(w[0] - w[1])] = 1 | 增加1号砝码后,放在天平异侧 |
同理,可以根据 f[i-1][j] 得到如下新状态:
新状态 | 状态产生原因 |
f[i][j] = 1 | 不使用 i 号砝码 |
f[i][j + w[i]] = 1 | 增加 i 号砝码后,放在 j 重量同侧 |
f[i][abs(j - w[i])] = 1 | 增加 i 号砝码后,放在 j 重量异侧 |
此处的 j 重量,可以理解为全在天平的某一侧(因为我们得到的是一个相对的重量值)
从而,我们可以从 f[0][ ] 一直推到 f[n-1][ ],代码如下:
for(int i = 1; i < n; i++)
{
for(int j = 1; j <= sum; j++)
{
if(f[i-1][j])
{
f[i][j] = 1;
f[i][j+w[i]] = 1;
f[i][int(abs(j-w[i]))] = 1;
}
}
}
然后累计 f[n-1][ ] 存在多少种状态即可!
源代码
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int N = 110, W = 1e5+10;
int n, w[N], f[N][W], sum = 0;
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
scanf("%d", &w[i]);
f[i][w[i]] = 1;
sum += w[i]; //得到总重量,即可能称量的重量最大值
}
for(int i = 1; i < n; i++) //枚举i(即求出使用0~i号砝码能得到的所有状态)
{
for(int j = 1; j <= sum; j++) //对所有可能的重量进行进行枚举
{
if(f[i-1][j]) //若使用0~i-1号砝码可以称量出j重量
{ //注意此处的j重量,可以理解为全在天平的某一侧
f[i][j] = 1; //那么不使用i号砝码也能称量出j重量
f[i][j+w[i]] = 1; //i号砝码放在j重量的同一侧
f[i][int(abs(j-w[i]))] = 1; //i号砝码放在j重量的另一侧
}
}
}
int cnt = 0;
for(int i = 1; i <= sum; i++) //累计f[n-1][]存在的状态,即所有可能称量的重量
cnt += f[n-1][i];
printf("%d", cnt);
return 0;
}