东华OJ进阶30盾神与砝码称重
问题描述
有一天,他在宿舍里无意中发现了一个天平!这个天平很奇怪,有n个完好的砝码,但是没有游码。盾神为他的发现兴奋不已!于是他准备去称一称自己的东西。他准备好了m种物品去称。神奇的是,盾神一早就知道这m种物品的重量,他现在是想看看这个天平能不能称出这些物品出来。但是盾神稍微想了1秒钟以后就觉得这个问题太难了,于是就丢给了你。
注意:砝码可以和物品一起放在天平的同一边。
输入说明
第一行为两个数,n和m。
第二行为n个数,表示这n个砝码的重量。
第三行为m个数,表示这m个物品的重量。
1<=n<=24, 1<=m<=10.
输出说明
输出m行,对于第i行,如果第i个物品能被称出,输出YES否则输出NO。
4 3
10 7 2 19
6 5 11
输出范例
NO
YES
YES
思路如下:
直接计算所有的情况会导致超时,故而采用分治的思想,将输入的砝码分为两个部分,然后分别求出两个部分的所有砝码放置可能出现的结果 sum1 和 sum2 集合。然后根据需要称量的结果 item,如果能在 sum2 中找到 item - sum1 的情况,那么就返回YES,否则就返回NO
代码实现
#include <iostream>
#include <vector>
#include <unordered_set>
using namespace std;
unordered_set<int> generate_sums(const vector<int>& ws) {
unordered_set<int> sums = {0};
for (int w : ws) {
unordered_set<int> temp;
for (int s : sums) {
temp.insert(s + w);
temp.insert(s - w);
temp.insert(s);
}
sums = temp;
}
return sums;
}
int main() {
int n, m;
cin >> n >> m;
vector<int> weights(n);
for (int i = 0; i < n; ++i) {
cin >> weights[i];
}
vector<int> items(m);
for (int i = 0; i < m; ++i) {
cin >> items[i];
}
int half = n / 2;
vector<int> first_part(weights.begin(), weights.begin() + half);
vector<int> second_part(weights.begin() + half, weights.end());
auto sum1 = generate_sums(first_part);
auto sum2 = generate_sums(second_part);
for (int item : items) {
bool found = false;
for (int s1 : sum1) {
if (sum2.count(item - s1)) {
found = true;
break;
}
}
cout << (found ? "YES" : "NO") << '\n';
}
return 0;
}
用例详细解释
输入:
4 3
10 7 2 19
6 5 11
执行过程
- 砝码分组
first_part = [10, 7]
second_part = [2, 19]
- 生成组合
sum1 是 first_part 的所有组合:
{0, 10, -10, 7, -7, 17, -17, 3, -3}
sum2 是 second_part 的所有组合:
{0, 2, -2, 19, -19, 21, -21, 17, -17}
- 检查物品
物品 6:
检查是否存在 s1 + s2 = 6。
例如:s1 = 10,s2 = -4(不存在)。
最终没有找到,输出 NO。
物品 5:
例如:s1 = 7,s2 = -2(存在)。
输出 YES。
物品 11:
例如:s1 = 10,s2 = 1(不存在)。
但 s1 = 7,s2 = 4(不存在)。
最终没有找到,输出 NO。