- 题意:给你n颗石子,这些石子可任意放在称的左边或右边(大的重量-小的重量),问是否可以得出所给的重量。
- 思路:01背包问题。
- 解析:
- 集合划分:f[i][j]表示前i个石子能否称出重量为j(1:代表可以,0:代表不行)
- 状态表示:
- f[0][0] = 1; //0颗石子可称出重量为0的情况必然存在
- 若前i-1颗石子可称出重量为j:
f[i][j] = 1; //不选i颗石子
f[i][j+w[i]]; //选第i颗石子放在称的右边(放在叠加一侧)
f[i][abs(j-w[i])]; //选第i颗石子放在称的左边(放在抵消一侧),假若是左侧重,则
可以计算出左侧的重量-右侧重量,否则计算出右侧重量-左侧重量,所以加绝对值。
- 代码:
#include<iostream>
#include<cstring>
using namespace std;
const int N = 110;
const int M = 1e4 + 5;
int f[N][M]; //前i个石子能否称出j的重量(1:可以, 0:不可以)
int n, t;
int w[N];
int main()
{
while(cin >> n)
{
for(int i = 1; i <= n; i++) cin >> w[i];
memset(f, 0, sizeof f);
f[0][0] = 1;
for(int i = 1; i <= n; i++)
{
for(int j = 0; j <= M; j++) //最多能称1e4重量(或者可对w[i]求一个前缀和)
{
if(f[i-1][j])
{
f[i][j] = 1; //不选择第i个石子
f[i][j+w[i]] = 1; //选择第i个石子放在右侧进行叠加
f[i][abs(j-w[i])] = 1; //选择第i个石子放在左侧进行抵消
}
}
}
cin >> t;
while(t --)
{
int x;
cin >> x;
if(x > 1e4 || !f[n][x]) cout << "NO" << endl;
else cout << "YES" << endl;
}
}
return 0;
}