Description
玛莎和比尔共同收藏了一批石头。现在他们想以相同的价值均分这批收藏的石头。如果这些石头的价值是相同的那就好办了,但是非常的遗憾的是,这些石头大小不一,美观程度也不一样,因此玛莎和比尔用1到6之间的自然数对每块石头赋予价值。现在他们以相同的价值对这些收藏的石头进行平分。
不幸的是,他们意识到即使这些石头的总价值是偶数的情况下,这方法有些时候也不能帮他们实现平分。例如,价值为1的石头有1块;价值为3的石头有1块;价值为4的石头有2块;就不能实现平分。现在他们要求你写一程序帮助他们核查他们的这些石头是否能够平分。
Input
输入数据第一行是一个正整数N(1 <= N <= 20),表示有N组数据需要判断。
接下来的N行表示N组数据。
每行输入描述了一组待平分的收藏石头。每行数据有6个非负整数构成,其中第i个表示价值为i的石头数量。因此,上述例子表示为“1 0 1 2 0 0”,石头总数不能超过200。
Output
对于每组收藏,如果可以平分输出YES,否则输出NO,每组测试结果用空行分割。
Sample Input
2
1 0 1 2 0 0
1 0 0 0 1 1
Sample Output
NO
YES
题解
这题数据量很小,暴搜也能过,这里提供一种动态规划的方法。
n个石头每个有选和不选两种状态,我们的目的是,判断是否可以选k个石头,使这k个石头的体积和剩余n-k个石头的体积相同。这可以转换成类似01背包的模型。定义dp[i][j]表示考虑到第i个石头,已经装了体积为j的石头的状态,dp[i][j]的值表示该状态是否合法,有如下状态转移方程。
d
p
[
i
]
[
j
]
=
{
d
p
[
i
]
[
j
]
∣
d
p
[
i
−
1
]
[
j
]
,
不
选
第
i
个
石
头
d
p
[
i
]
[
j
]
∣
d
p
[
i
−
1
]
[
j
−
v
a
l
u
e
[
i
]
]
,
选
第
i
个
石
头
dp[i][j]=\left\{ \begin{aligned} dp[i][j]\mid dp[i-1][j], 不选第i个石头\\ dp[i][j]\mid dp[i-1][j-value[i]],选第i个石头 \end{aligned} \right.
dp[i][j]={dp[i][j]∣dp[i−1][j],不选第i个石头dp[i][j]∣dp[i−1][j−value[i]],选第i个石头
那么只需计算dp[rocks.size()][tot_value/2]的值即可。
代码
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
bool dp[205][1500];
int main() {
int t;
cin >> t;
while (t--) {
memset(dp, 0, sizeof dp);
vector<int>rocks;
int tot = 0;
for (int i = 1; i <= 6; ++i) {
int cnt;
cin >> cnt;
for (int j = 0; j < cnt; ++j) {
rocks.push_back(i);
tot += i;
}
}
if (tot & 1) {
cout << "NO" << endl;
continue;
}
int n = rocks.size();
for (int i = 1; i <= n; ++i) {
dp[i][rocks[i - 1]] = 1;
for (int j = 0; j <= tot; ++j) {
dp[i][j] |= dp[i - 1][j];
if (j - rocks[i - 1] >= 0) {
dp[i][j] |= dp[i - 1][j - rocks[i - 1]];
}
}
}
cout << (dp[n][tot / 2] ? "YES" : "NO") << endl;
}
}