2021 RoboCom 世界机器人开发者大赛-本科组(初赛)
7-1 懂的都懂
题目简述:
\(\qquad\) 给定一张由 \(N\) 个正整数组成的“原图”,定义当组成一张“新图”的每一个整数都能由“原图”的 \(N\) 个正整数中的任意四个的平均数组成时,这张“新图”与“原图”相似。
\(\qquad\) 询问在 \(K\) 张图中哪些与“原图”相似,而哪些不相似。
思路:
\(\qquad\) 考虑到 \(N\) 非常小,因此可以直接枚举任意四个正整数组成平均数的每一种情况并储存在一个 \(set\) 中。
\(\qquad\) 在处理 \(K\) 次询问时我们直接遍历其中的 \(M\) 个整数并查找是否在 \(set\) 中即可。
code:
#include <bits/stdc++.h>
int main() {
int n, t;
std::cin >> n >> t;
std::vector<int> a(n);
for(auto &it : a) {
std::cin >> it;
}
std::set<int> alls;
for(int i = 0; i < n; i ++ ) {
for(int j = i+1; j < n; j ++ ) {
for(int k = j+1; k < n; k ++ ) {
for(int l = k+1; l < n; l ++ ) {
alls.insert(a[i]+a[j]+a[k]+a[l]);
}
}
}
}
while(t -- ) {
int m;
std::cin >> m;
bool fg = true;
for(int i = 0; i < m; i ++ ) {
int x;
std::cin >> x;
if(alls.find(x*4) == alls.end()) {
fg = false;
}
}
std::cout << (fg ? "Yes\n" : "No\n");
}
}
7-2 芬兰木棋
题目简述:
\(\qquad\) 在坐标系中存在若干个旗子,哲哲位于 \((0, 0)\) 可以往任意方向击倒任意个旗子,如果击倒一个旗子则获得旗子的得分,击倒多个旗子则获得与击倒数目相同的得分。
\(\qquad\) 问:最多可以获得多少分,以及获得最大分数的最少击倒次数是多少。
思路:
\(\qquad\) 注意到,可以获得的最大分数一定是所有旗子的分数和,而当多个连续的,分数为 \(1\) 的旗子处于同一直线时,可以使用一次操作使其全部击倒。
\(\qquad\) 同时,我们考虑模拟哲哲击倒旗子的过程来得到答案:
\(\qquad\) 1. 我们假设哲哲每次在击倒完一个方向上的所有旗子之后,才会击倒下一个方向上的旗子。因此我们可以将所有旗子按照方向储存,首先按照四个象限划分大方向,接着按照斜率来划分具体的方向。
\(\qquad\) 2. 接着,模拟击倒一个方向上所有旗子的过程,由上面的分析我们可以得知,当旗子的分数 \(score \gt 1\) 时,我们需要消耗一次单独的操作次数来击倒它,而当旗子的分数 \(score = 1\) 时,可以分成两种情况,第一种,上一面击倒的旗子分数不为 \(1\) ,那么我们肯定需要使用一次操作次数来击倒这面旗子;而当上一面击倒的旗子分数也等于 \(1\) 时,我们就可以将这一面旗子并入上一面旗子的操作次数中与上一面旗子同时被击倒。
code:
#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fir first
#define sec second
#define all(x) x.begin(), x.end()
int gcd(int a, int b) {
while(b) {
int r = a % b;
a = b, b = r;
}
return a;
}
int get_step(std::vector<pii>& locs, std::map<pii, int>& scores) {
int res = 0;
std::sort(all(locs), [&](pii& a, pii& b){
if(a.fir != 0) return a.fir < b.fir;
return a.sec < b.sec;
});
int last = 0;
for(auto& loc : locs) {
if(scores[loc] != 1) {
res ++;
}
else {
if(last != 1) res ++;
}
last = scores[loc];
}
return res;
}
signed main() {
int n;
std::cin >> n;
std::map<pii, std::vector<pii>> leftup, rightup, leftdown, rightdown;
std::map<pii, int> scores;
int res = 0, step = 0;
for(int i = 0; i < n; i ++ ) {
int x, y, w;
std::cin >> x >> y >> w;
res += w;
int g = gcd(x, y);
pii k = {x/g, y/g};
if(x >= 0 && y >= 0) rightup[k].emplace_back(x, y);
else if(x <= 0 && y >= 0) leftup[k].emplace_back(x, y);
else if(x <= 0 && y <= 0) leftdown[k].emplace_back(x, y);
else rightdown[k].emplace_back(x, y);
scores[{x, y}] = w;
}
for (auto &[k, locs] : leftup) {
step += get_step(locs, scores);
}
for (auto &[k, locs] : leftdown) {
step += get_step(locs, scores);
}
for (auto &[k, locs] : rightup) {
step += get_step(locs, scores);
}
for (auto &[k, locs] : rightdown) {
step += get_step(locs, scores);
}
std::cout << res << ' ' << step;
}
7-3 打怪升级
题目简述
\(\qquad\) 在 \(N\) 个堡垒之间有 \(M\) 条道路,而每条道路都有一个怪兽看守,对于第 \(M_i\) 条道路击败这个怪物需要 \(w_i\) 点能量,并获得价值为 \(val_i\) 的武器。
\(\qquad\) 问:玩家在哪一个城堡开始,可以用最少的能量攻克需要能量最多的城堡,并获得最大的武器价值。
思路:
\(\qquad\) 可以发现这是一个最短路问题,同时加上了一个武器价值最大的限制条件。因此我们只需要在判断最短路的同时判断最大的武器价值即可。
\(\qquad\) 这题我用的是 \(dijkstra\) 算法,先对每个点跑一遍 \(dijkstra\) 找出符合题意的起点,然后单独对这个起点跑一遍 \(dijkstra\) 然后处理询问即可。
\(\qquad\) 注意要使用前驱数组储存路径,因为题目要求输出从起点到询问点的路径。只需要储存每个点由哪个点到达,然后递归输出即可。
#include <bits/stdc++.h>
struct Edge {
int v, w, val;
};
struct Node {
int dis, vis, id;
bool operator>(const Node& a) const {
if(dis != a.dis) return dis > a.dis;
return vis < a.vis;
}
};
int main() {
int n, m;
std::cin >> n >> m;
std::vector<Edge> edges[n+1];
for(int i = 0; i < m; i ++ ) {
int u, v, w, val;
std::cin >> u >> v >> w >> val;
edges[u].emplace_back(v, w, val);
edges[v].emplace_back(u, w, val);
}
int max_dis, max_val;
std::vector<int> dis(n+1), vals(n+1);
std::vector<bool> vis(n+1);
std::vector<int> pre(n+1);
std::priority_queue<Node, std::vector<Node>, std::greater<Node>> q;
auto dijkstra = [&](int s) -> void {
max_dis = 0, max_val = 0;
for(int i = 1; i <= n; i ++ ) {
dis[i] = INT_MAX;
vals[i] = 0;
vis[i] = pre[i] = 0;
}
dis[s] = 0;
q.emplace(0, 0, s);
while(q.size()) {
int u = q.top().id;
q.pop();
if(vis[u]) continue;
vis[u] = 1;
for(auto edge : edges[u]) {
int v = edge.v, w = edge.w, val = edge.val;
if(vis[v]) continue;
if(dis[v] > dis[u]+w || (dis[v] == dis[u]+w && vals[v] < vals[u]+val)) {
dis[v] = dis[u]+w;
vals[v] = vals[u]+val;
pre[v] = u;
q.emplace(dis[v], vals[v], v);
}
}
if(dis[u] > max_dis || (dis[u] == max_dis && vals[u] > max_val)) {
max_dis = dis[u], max_val = vals[u];
}
}
};
int st, min = INT_MAX;
for(int i = 1; i <= n; i ++ ) {
dijkstra(i);
if(min > max_dis) {
st = i;
min = max_dis;
}
}
int t;
std::cin >> t;
dijkstra(st);
std::cout << st << '\n';
auto print = [&](auto& print, int ed) -> void {
if(pre[ed] == 0) {
std::cout << ed;
return;
}
print(print, pre[ed]);
std::cout << "->" << ed;
return;
};
while(t -- ) {
int ed;
std::cin >> ed;
print(print, ed);
std::cout << '\n';
std::cout << dis[ed] << ' ' << vals[ed] << '\n';
}
}
7-4 疫情防控
题目简述:
\(\qquad\) 有 \(N\) 座机场,\(M\) 条航线,在接下来的 \(D\) 天中每天会封禁一座机场,同时每一天会有 \(Q\) 段航程。
\(\qquad\) 问:在每一天的航程中有多少航程因为封禁而无法通行。
思路:
\(\qquad\) 连通性问题,优先考虑并查集做法。
\(\qquad\) 而通过模拟我们发现,每天封禁一个机场相当于每天从并查集中删除一个点以及所联通的边,实现起来过于复杂。因此考虑离线算法反向构造并查集。
\(\qquad\) 从最后一天开始构造并查集,每往前一天相当于在并查集中添加一个点和相应的边。一边构造并查集一边处理询问,并将询问的答案存入一个答案数组中再按照询问的顺序输出即可。
#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fir first
#define sec second
#define all(x) x.begin(), x.end()
struct DSU {
std::vector<int> f, siz;
DSU() {}
DSU(int n) { init(n); }
void init(int n) { f.resize(n); std::iota(all(f), 0); siz.assign(n, 1); }
int find(int x) {
while(x != f[x]) x = f[x] = f[f[x]];
return x;
}
bool same(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
x = find(x), y = find(y);
if(x == y) return false;
siz[x] += siz[y];
f[y] = x;
return true;
}
int size(int x) {
return siz[find(x)];
}
};
int main() {
int n, m, d;
std::cin >> n >> m >> d;
DSU dsu(n+1);
std::vector<int> edges[n+1];
for(int i = 0; i < m; i ++ ) {
int u, v;
std::cin >> u >> v;
edges[u].emplace_back(v);
edges[v].emplace_back(u);
}
std::vector<int> vis(n+1, 0), c(d+1);
std::vector<pii> query[d+1];
for(int i = 1; i <= d; i ++ ) {
int q;
std::cin >> c[i] >> q;
vis[c[i]] = 1;
for(int j = 1; j <= q; j ++ ) {
int x, y;
std::cin >> x >> y;
query[i].emplace_back(x, y);
}
}
for(int u = 1; u <= n; u ++ ) {
if(vis[u]) continue;
for(auto v : edges[u]) {
if(vis[v]) continue;
dsu.merge(u, v);
}
}
std::vector<int> ans(d+1);
for(int i = d; i >= 1; i -- ) {
int res = 0;
vis[c[i]] = 0;
for(int j = 0; j < query[i].size(); j ++ ) {
if(!dsu.same(query[i][j].fir, query[i][j].sec)) res ++;
}
ans[i] = res;
for(auto v : edges[c[i]]) {
if(vis[v]) continue;
dsu.merge(c[i], v);
}
}
for(int i = 1; i <= d; i ++ ) {
std::cout << ans[i] << '\n';
}
}

浙公网安备 33010602011771号