HJ41.称砝码
解法一(回溯法,超过时间限制):
#include <iostream>
#include <vector>
#include <unordered_set>
using namespace std;
unordered_set<int> rst;
int sum(const vector<int>& vec) {
int ret = 0;
for (auto& i : vec) ret += i;
return ret;
}
void backtrack(const vector<int>& weights, vector<int>& path, int cur) {
rst.insert(sum(path));
if (path.size() == weights.size()) {
return;
}
unordered_set<int> vis;
for (int i = cur; i < weights.size(); ++i) {
if (vis.count(weights[i]) == 0) {
vis.insert(weights[i]);
path.emplace_back(weights[i]);
backtrack(weights, path, i + 1);
path.pop_back();
}
}
}
int weightMethod(vector<int> weights) {
vector<int> path {};
backtrack(weights, path, 0);
return (int)rst.size();
}
vector<int> spread(const vector<int>& weight, const vector<int>& num, int n) {
vector<int> weights;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < num[i]; ++j) {
weights.emplace_back(weight[i]);
}
}
return weights;
}
int main() {
int n = 0, temp = 0;
vector<int> weight, num;
while (cin >> n) {
for (int i = 0; i < n; ++i) {
cin >> temp;
weight.emplace_back(temp);
}
for (int i = 0; i < n; ++i) {
cin >> temp;
num.emplace_back(temp);
}
cout << weightMethod(spread(weight, num, n)) << endl;
weight.clear();
num.clear();
rst.clear();
}
return 0;
}
解法二:
#include <iostream>
#include <vector>
#include <unordered_set>
using namespace std;
/*
0 -> 0
+1 0 1 -> 0 1
+1 0 1 1 2 -> 0 1 2
+2 0 1 2 2 3 4 -> 0 1 2 3 4
*/
int weightMethod(const vector<int>& weights) {
unordered_set<int> rst { 0 };
for (auto& i : weights) {
unordered_set<int> temp(rst);
for (auto& j : temp) {
rst.insert(i + j);
}
}
return (int)rst.size();
}
// 展开输入,摊成一个数组
vector<int> spread(const vector<int>& weight, const vector<int>& num) {
vector<int> weights;
for (int i = 0; i < (int)weight.size(); ++i) {
for (int j = 0; j < num[i]; ++j) {
weights.emplace_back(weight[i]);
}
}
return weights;
}
int main() {
int n = 0;
while (cin >> n) {
vector<int> weight(n), num(n);
for (auto& i : weight) cin >> i;
for (auto& i : num) cin >> i;
cout << weightMethod(spread(weight, num)) << endl;
}
return 0;
}
解题思路:
难点1:熟悉回溯的话可以想到解法一,但很容易超过时空限制,题目只是要求最终的结果,并不需要每种方式的排列方式,所以没必要使用回溯;
难点2:不容易在考试的时候就能看出以下规律,看出的话基本能想到使用set
来求解了;
/*
0 -> 0
+1 0 1 -> 0 1
+1 0 1 1 2 -> 0 1 2
+2 0 1 2 2 3 4 -> 0 1 2 3 4
*/
知识点:
知识点1:vector
可以确定大小之后直接写入,,,学习到了;
int n = 0;
while (cin >> n) {
vector<int> weight(n), num(n);
for (auto& i : weight) cin >> i;
for (auto& i : num) cin >> i;
cout << weightMethod(spread(weight, num)) << endl;
}
知识点2:set
里面有set_intersection
(取集合交集)、set_union
(取集合并集)、set_difference
(取集合差集)、set_symmetric_difference
(取集合对称差集)等函数,包含在algorithm
头文件当中;
#include <set> // std::set
#include <algorithm> // std::set_union
std::set<int> set1 { 1, 2, 3, 4, 5, 6 }; // Contains 6 5 4 3 2 1
std::set<int> set2 { 4, 5, 6, 7, 8, 9 }; // Contains 9 8 7 6 5 4
std::set<int> result; // Elements in descending sequence
std::set_union(std::begin(set1), std::end(set1),
std::begin(set2), std::end(set2),
std::inserter(result, std::begin(result)));