2021 RoboCom 世界机器人开发者大赛-本科组(初赛)

2021 RoboCom 世界机器人开发者大赛-本科组(初赛)

7-1 懂的都懂 分数 20

b3ceb051352ac65c29767cc3ecf2b21192138add.jpg

众所周知,在互联网上有很多话是不好直接说出来的,不过一些模糊的图片仍然能让网友看懂你在说什么。然而对这种言论依然一定要出重拳,所以请你实现一个简单的匹配算法。

现在我们采集了原图的一些特征数据,由 N 个小于 255 的非负整数组成,假设对于给定的若干张由 M**i 个同样小于 255 的非负整数组成的新图的特征数据,每个数据都可以由原图中任意四个不同数据的平均值计算而来,则称新图为原图的相似图片。对于给出的数据,请你判断是不是相似图片。

注意,不同数据指的并非是数据的值不同,而是不能取同一个数据多次。对于两个相同值的数据,如果给出两次,则可以取两次。

输入格式:

输入第一行是两个整数 N,K (1 ≤ N ≤ 50, 1 ≤ K ≤ 200),表示采集的原图的特征数据个数和新图的张数。

接下来一行为 N 个小于 255 的非负整数,表示原图的特征数据。

最后的 K 行,每行第一个数是 M**i (1 ≤ M**i ≤ 200),表示新图的特征数据个数。然后是 M**i 个小于 255 的非负整数,表示新图的特征数据。

输出格式:

对于每一张新图,如果为相似图片,则在一行中输出 Yes,否则输出 No。

输入样例:

5 3
4 8 12 20 40
3 11 16 19
3 12 16 19
10 11 11 11 11 11 11 11 11 11 11

输出样例:

Yes
No
Yes

代码长度限制16 KB

时间限制400 ms

内存限制64 MB

代码:

#include <bits/stdc++.h>

using namespace std;

#define N 10000

int num[N], s[N];

int main()

{

int n, k;

cin >> n >> k;

for (int i = 0; i < n; i++)

cin >> num[i];

for (int a = 0; a < n; a++)

for (int b = a + 1; b < n; b++)

for (int c = b + 1; c < n; c++)

for (int d = c + 1; d < n; d++)

s[num[a] + num[b] + num[c] + num[d]] = 1;

for (int i = 0; i < k; i++)

{

int t;

cin >> t;

int flag = 1;

for (int j = 0; j < t; j++)

{

int x;

cin >> x;

if (s[x * 4] == 0)

flag = 0;

}

if (flag)

puts("Yes");

else

puts("No");

}

}

7-2 芬兰木棋分数 25分

WX20200212-152528.png

芬兰木棋(Mölkky,又称芬兰木柱)是源自芬兰的一项运动。哲哲将这个运动改造成了赛博朋克单人版,现在场上一开始有 N 根立起的小木棋(上面分别标有一个非负整数),哲哲投掷一根大木棋去击倒这些小木棋以获得分数。分数规则如下:

  • 如果仅击倒 1 根木棋,则得木棋上的分数。
  • 如果击倒 2 根或以上的木棋,则只得击倒根数的分数。(例如击倒 5 根,则得 5 分。)

哲哲固定站在 (0,0) 点上,四周放着若干个小木棋 (X**i,Y**i),坐标均为整数。每次哲哲可以朝一个方向扔出大木棋,大木棋会打倒这个方向上离哲哲最近的 k 个小木棋。哲哲游戏水平很高超,所以这个 k 可以自由控制。

请问哲哲最多能拿多少分,在获得最多分数的情况下最少需要扔出多少次大木棋?

规则与真实规则有较大出入,真实游玩时请以国际莫尔基组织的规则为准

输入格式:

输入第一行是一个正整数 N (1 ≤ N ≤ 105),表示场上一开始有 N 个木棋。

接下来 N 行,每行 3 个整数 X**i,Y**i,P**i,分别表示木棋放置在 (X**i,Y**i),木棋上的分数是 P**i。坐标在 32 位整数范围内,分数为小于等于 1000 的正整数。

保证 (0,0) 点没有木棋,也没有木棋重叠放置。

输出格式:

输出一行两个数,表示最多分数以及获得最多分数最少需要投掷大木棋多少次。

输入样例:

11
1 2 2
2 4 3
3 6 4
-1 2 2
-2 4 3
-3 6 4
-1 -2 1
-2 -4 1
-3 -6 1
-4 -8 2
2 -1 999

输出样例:

1022 9

代码:

#include <bits/stdc++.h>

using namespace std;

#define N 100010

struct node

{

int x, y;

int val;

} m[N];

bool cmp(node a, node b)

{

if (a.x == b.x)

return a.y < b.y;

return a.x < b.x;

}

int main()

{

// freopen("2.txt", "r", stdin);

int n;

cin >> n;

int sum = 0;

for (int i = 0; i < n; i++)

{

cin >> m[i].x >> m[i].y >> m[i].val;

sum += m[i].val;

}

sort(m, m + n, cmp); // 进行一次排序

int toge = 0;

map<pair<int, int>, int> mp;

for (int i = 0; i < n; i++)

{

int g = abs(__gcd(m[i].x, m[i].y));

int x = m[i].x / g, y = m[i].y / g;

if (m[i].val != 1)

mp[{x, y}] = 0; // 切断

else // 合为一起

{

if (mp[{x, y}] == 0)

mp[{x, y}] = 1;

else

toge++;

// cout<<m[i].x<<" "<<m[i].y<<endl;

}

}

// cout << toge << endl;

cout << sum << ' ' << n - toge << endl;

}

7-3 打怪升级 分数 25

dgsj.JPG

很多游戏都有打怪升级的环节,玩家需要打败一系列怪兽去赢取成就和徽章。这里我们考虑一种简单的打怪升级游戏,游戏规则是,给定有 N 个堡垒的地图,堡垒之间有道路相连,每条道路上有一只怪兽把守。怪兽本身有能量,手里的武器有价值。打败怪兽需要的能量等于怪兽本身的能量,而怪兽一旦被打败,武器就归玩家所有 —— 当然缴获的武器价值越高,玩家就越开心。

你的任务有两件:

​ 1.帮助玩家确定一个最合算的空降位置,即空降到地图中的某个堡垒,使得玩家从这个空降点出发,到攻下最难攻克(即耗费能量最多)的那个堡垒所需要的能量最小;

​ 2.从这个空降点出发,帮助玩家找到攻克任意一个其想要攻克的堡垒的最省能量的路径。如果这种路径不唯一,则选择沿途缴获武器总价值最高的解,题目保证这种解是唯一的。

输入格式:

输入第一行给出两个正整数 N (≤1000) 和 M,其中 N 是堡垒总数,M 是怪兽总数。为简单起见,我们将堡垒从 1 到 N 编号。随后 M 行,第 i 行给出了第 i 只怪兽的信息,格式如下:

B1 B2 怪兽能量 武器价值

其中 B1B2 是怪兽把守的道路两端的堡垒编号。题目保证每对堡垒之间只有一只怪兽把守,并且 怪兽能量武器价值 都是不超过 100 的正整数。

再后面是一个正整数 K(≤N)和玩家想要攻克的 K 个目标堡垒的编号。

输出格式:

首先在一行中输出玩家空降的堡垒编号 B0。如果有多种可能,则输出编号最小的那个。

随后依次为玩家想要攻克的每个堡垒 B 推荐最省能量的攻克路径,并列出需要耗费的能量值和沿途缴获武器的总价值。注意如果最省力的路径不唯一,则选择沿途缴获武器总价值最高的解。格式为:

B0->途经堡垒1->...->B
总耗费能量 武器总价值

输入样例:

6 12
1 2 10 5
2 3 16 20
3 1 4 2
2 4 20 22
4 5 2 2
5 3 12 6
4 6 8 5
6 5 10 5
6 1 20 25
1 5 8 5
2 5 2 1
2 6 8 5
4
2 3 6 5

输出样例:

5
5->2
2 1
5->1->3
12 7
5->4->6
10 7
5
0 0

代码长度限制16 KB

时间限制5000 ms

内存限制64 MB

代码:

#include <bits/stdc++.h>`

using namespace std;

#define int long long

#define N 1010

int dis1[N], dis2[N], vis[N]; // dis1记录最短距离,dis2记录最短距离下的最大权值和

int pre[N]; // 记录路径

struct node

{

int v, w1, w2;

};

int e[N][N]; // e记录所有点对最短路

node g[N][N]; // g记录边权

void print(int x)

{

if (pre[x] == 0)

return;

print(pre[x]);

cout << "->" << x;

}

signed main()

{

// 初始化

memset(e, 0x3f, sizeof(e));

memset(g, 0x3f, sizeof(g));

int n, m;

cin >> n >> m;

for (int i = 1; i <= m; i++)

{

int u, v, w1, w2;

cin >> u >> v >> w1 >> w2;

e[u][v] = e[v][u] = min(e[u][v], w1);

g[u][v].v = v;

g[u][v].w1 = g[v][u].w1 = min(g[u][v].w1, w1);

g[u][v].w2 = g[v][u].w2 = min(g[u][v].w2, w2);

}

// floyd跑所有点对最短路

for (int k = 1; k <= n; k++)

for (int i = 1; i <= n; i++)

for (int j = 1; j <= n; j++)

e[i][j] = min(e[i][j], e[i][k] + e[k][j]);

int id = 0, mi = 0x3f3f3f3f; // 记录最小的最大距离节点,最小的最大距离

for (int i = 1; i <= n; i++)

{

int tmp = 0;

for (int j = 1; j <= n; j++)

tmp = max(tmp, e[i][j]);

if (tmp < mi)

mi = tmp, id = i;

}

// cout << id << endl;

// dijkstra跑单源最短路

memset(dis1, 0x3f, sizeof(dis1));

// memset(dis2, 0x3f, sizeof(dis2));

memset(vis, 0, sizeof(vis));

dis1[id] = dis2[id] = 0;

for (int i = 1; i <= n; i++)

{

int t = -1;

for (int j = 1; j <= n; j++)

{

if (!vis[j] && (t == -1 || dis1[j] < dis1[t]))

t = j;

else if (!vis[j] && dis1[j] == dis1[t] && dis2[j] < dis2[t])

t = j;

}

vis[t] = 1;

for (int j = 1; j <= n; j++)

{

if (dis1[j] > dis1[t] + g[t][j].w1)

{

dis1[j] = dis1[t] + g[t][j].w1;

dis2[j] = dis2[t] + g[t][j].w2;

pre[j] = t;

}

else if (dis1[j] == dis1[t] + g[t][j].w1 && dis2[j] < dis2[t] + g[t][j].w2)

{

dis2[j] = dis2[t] + g[t][j].w2;

pre[j] = t;

}

}

}

// 输出路径

cout << id << endl;

int t;

cin >> t;

while (t--)

{

int x;

cin >> x;

cout << id;

print(x);

cout << endl;

cout << dis1[x] << " " << dis2[x] << endl;

}

}

7-4 疫情防控 分数 30

疫情尚未结束,严防疫情反复。为了做好疫情防控工作,国内设置了地区风险等级,对于中高风险地区的人员采取限制移动、居家隔离等手段。

为了研究疫情防控对于跨地区交通运输的影响,假设现在有 N 个机场,M 条航线,每天都会新增一个防控地区,一个防控地区会导致一个机场无法正常运作,航线也自然无法正常运行,每天会有 Q**i 对旅客从 X**i 机场前往 Y**i 机场,请计算有多少对旅客会受到影响无法完成行程。

旅客只要能直达或通过若干次中转,且乘坐的所有航线的出发和到达机场都正常运作,即视作可完成行程。

输入格式:

输入第一行是三个整数 N,M,D (1≤N≤5×104, 1≤M≤2×105, 1≤D≤103), 表示机场数、航线数以及新增防控地区的天数。

接下来首先有 M 行,每行给出空格分隔的两个数字 AB,表示编号为 AB 的机场之间有一条航线。航线是双向的,机场编号从 1 到 N

然后是 D 块输入,每块输入内第一行为空格分隔的两个整数 CQ (1≤Q≤103),表示新增机场编号为 C 所在的城市为防控地区,今天有 Q 段行程。数据保证新增的城市之前一定不是防控地区。

接下来的 Q 行,每行是空格分隔的两个数字 XY,表示编号为 XY 的机场的一段行程。行程有可能包括之前就已经成为防控地区的城市。

输出格式:

对于每天的询问,请在一行中输出在新增了一个防控地区后当天的行程有多少不能成行。

输入样例:

5 5 3
1 2
1 3
1 5
2 5
3 4
4 3
1 3
1 4
2 3
5 3
3 4
2 3
3 5
1 3
2 3
2 5
3 4

输出样例:

1
2
3

代码:

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b) for (int i = a; i <= b; i++)

#define int long long

#define N 50050

int fa[N]; // 父节点

pair<int, int> e[200020]; // 存储边

vector<int> g[N]; // 存储图

int del[N], flag[N]; // 存储和标记删掉的点

vector<pair<int, int>> qs[N]; // 存储行程

int res[N]; // 存储答案

int find(int x) // 并查集

{

if (x != fa[x])

fa[x] = find(fa[x]);

return fa[x];

}

void merge(int x, int y) // 合并

{

int fx = find(x), fy = find(y);

if (fx != fy)

fa[fx] = fy;

}

signed main()

{

rep(i, 1, N - 1)

fa[i] = i; // 初始化并查集

int n, m, d; // 点/边/删掉的边

cin >> n >> m >> d;

rep(i, 1, m) // 读边

{

int u, v;

cin >> u >> v;

e[i] = {u, v}; // 存储边

g[u].push_back(v); // 存储图

g[v].push_back(u);

}

rep(i, 1, d) // 读删点和行程

{

int c, q;

cin >> c >> q; // 删掉的点和行程个数

del[i] = c; // 存储删掉的点

flag[c] = 1; // 标记删掉的点

while (q--)

{

int u, v;

cin >> u >> v; // 行程

qs[i].push_back({u, v});

}

}

// 开始处理,逆向加边

rep(i, 1, m)

{

int u = e[i].first, v = e[i].second;

if (!flag[u] && !flag[v]) // 如果两个点都没有被删掉

merge(u, v);

}

// 先计算每次删除后不能通过的行程数,在将删除的点加入并查集

for (int i = d; i >= 1; i--)

{

// 统计不能通过的行程数

int cnt = 0;

for (auto x : qs[i])

{

int u = x.first, v = x.second;

if (flag[u] || flag[v] || find(u) != find(v))

{

cnt++;

// cout << u << " " << v << endl;

}

}

// cout << cnt << endl;

res[i] = cnt;

// 合并

int c = del[i]; // 逆向处理

flag[c] = 0; // 标记

for (auto x : g[c])

if (!flag[x])

merge(c, x);

}

rep(i, 1, d)

cout

<< res[i] << endl;

}

posted @ 2023-07-17 16:33  fengxinyuan  阅读(68)  评论(0)    收藏  举报