2021 RoboCom 世界机器人开发者大赛-本科组(复赛)
7-1 冒险者分队
思路:
\(\qquad\)首先,我们把初始数值与目标数值做差,通过题目的两种操作不难得知,如果差值不是20的倍数那么一定无法成功。
\(\qquad\)同时,我们把差值全部除以20,以简化问题。接着,我们思考一下,我们将三个差值排序之后可以得到 \(diff_1 \lt diff_2 \lt diff_3\) 的排列,而我们的目标也变成了通过最少次操作使这三个差值的关系变为 \(diff_1 = diff_2 = diff_3 = 0\) 而通过这个结果我们倒推可以发现,最终一定是由 \(-1, -1, 2\) 或 \(-2, 1, 1\) 的排列通过操作的来。
\(\qquad\)而此时我们发现,这两种排列的三个数在模 \(3\) 的情况下是相等的,而根据同余原理倒推我们可以发现不论何时他们在模 \(3\) 意义下的结果一定相等。根据这点我们完成了最后的不可行判断。
\(\qquad\)最后,如何来构造最优解呢?首先,我们对 \(diff_2\) 以最少的操作次数置零,然后对于 \(diff_1\) 以及 \(diff_3\) 我们可以通过操作一和操作二混合使用的方式来达到 \(diff_1 + 3, diff_2 + 0, diff_3 - 3\) 的效果,并且可以证明这是最优解。
code:
#include <bits/stdc++.h>
#define int long long
void solve() {
int a[3], b[3];
int sum_a = 0, sum_b = 0;
for(int i = 0; i < 3; i ++ ) {
std::cin >> a[i];
sum_a += a[i];
}
for(int i = 0; i < 3; i ++ ) {
std::cin >> b[i];
sum_b += b[i];
a[i] -= b[i];
}
if(a[0] % 20 || a[1] % 20 || a[2] % 20 || sum_a != sum_b) {
std::cout << "-1\n";
return;
}
a[0] /= 20, a[1] /= 20, a[2] /= 20;
std::sort(a, a+3);
int mod = (a[0] % 3 + 3) % 3;
if((a[1] % 3 + 3) % 3 != mod || (a[2] % 3 + 3) % 3 != mod) {
std::cout << "-1\n";
return;
}
int ans = 0;
if(a[1] > 0) {
ans += a[1], a[2] -= a[1], a[0] += a[1]*2;
}
else {
ans += -a[1], a[0] += -a[1], a[2] += a[1]*2;
}
std::cout << ans + (a[2] / 3) * 2 << '\n';
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);std::cout.tie(0);
int t = 1;
std::cin >> t;
while(t -- ) {
solve();
}
return 0;
}
7-2 拼题A打卡奖励
思路:
\(\qquad\)这道题我刚好在校赛出给你们做过,一道很经典的01背包变种,相较于普通的01背包,需要注意的就是因为时间是以秒为单位计数的,所以当枚举时间的时候一定会超时。而我们注意到金币的数据范围非常小,因此我们设 \(f[i]\) 为当枚举到一道题时,得到 \(i\) 枚金币需要花费的最少时间。由此可得转移方程 $$f[i] = min(f[i], f[i-c]+t)$$
code:
#include <bits/stdc++.h>
#define int long long
const int inf = 1e9;
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int n, m;
std::cin >> n >> m;
int max = 0;
std::vector<int> t(n), c(n);
for(int i = 0; i < n; i ++ ) {
std::cin >> t[i];
}
for(int i = 0; i < n; i ++ ) {
std::cin >> c[i];
max += c[i];
}
std::vector<int> f(max+1, inf);
f[0] = 0;
for(int i = 0; i < n; i ++ ) {
for(int j = max; j >= c[i]; j -- ) {
f[j] = std::min(f[j], f[j-c[i]]+t[i]);
}
}
for(int i = max; i >= 0; i -- ) {
if(f[i] <= m) {
std::cout << i << '\n';
return 0;
}
}
}
7-3 快递装箱
思路:
\(\qquad\) 一道题目非常繁琐的模拟题,思路都在草稿纸上了:
code:
#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fir first
#define sec second
#define all(x) x.begin(), x.end()
std::deque<pii> A, D;
std::queue<pii> B, C;
int n, w_max, w1, w2;
int ans1 = 0, ans2 = 0;
std::vector<int> ans;
void work() {
if(D.size()) {
auto top = D.front();
if(D.size() == 1) {
if(!top.sec) {
D.pop_front();
D.push_front({top.fir, 1});
}
}
else {
D.pop_front();
auto next = D.front();
if(!next.sec && next.fir+top.fir <= w_max) {
D.pop_front();
D.push_front({next.fir+top.fir, 1});
}
else {
if(top.fir > w2) ans2 ++;
else A.push_back(top);
}
}
if(!D.front().sec) {
top = D.front();
D.pop_front();
D.push_front({top.fir, 1});
}
}
if(C.size()) {
if(C.front().fir > w2 && C.front().sec) ans1 ++;
else D.push_back(C.front());
C.pop();
}
if(B.size()) {
if(B.front().fir > w1) C.push({B.front().fir, 1});
else C.push(B.front());
B.pop();
}
if(A.size()) {
B.push(A.front());
A.pop_front();
}
}
int main() {
std::cin >> n >> w_max >> w1 >> w2;
for(int i = 0; i < n; i ++ ) {
int w;
std::cin >> w;
if(A.empty()) A.push_front({w, 0});
else {
auto top = A.front();
if(top.fir+w <= w_max) {
A.pop_front();
A.push_front({top.fir+w, 1});
}
else {
A.push_front({w, 0});
}
}
work();
}
for(int i = 1; i <= 10010; i ++ ) work();
while(D.size()) {
auto top = D.front(); D.pop_front();
if(top.fir > w2) ans2 ++;
else A.push_back(top);
}
while(A.size()) ans.push_back(A.front().fir), A.pop_front();
while(B.size()) ans.push_back(B.front().fir), B.pop();
while(C.size()) ans.push_back(C.front().fir), C.pop();
while(D.size()) ans.push_back(D.front().fir), D.pop_front();
std::sort(all(ans));
std::cout << ans1 << ' ' << ans2 << ' ' << ans.size() << '\n';
if(ans.size()) for(int i = 0; i < ans.size(); i ++ ) {
if(i == 0) std::cout << ans[i];
else std::cout << ' ' << ans[i];
}
else {
std::cout << "None";
}
}