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

发布时间 2023-07-17 16:25:44作者: fengxinyuan

RC-u1 不要浪费金币 分数 10

哲哲最近在玩一个游戏,击杀怪物能获得金币 —— 这里记击杀第 i 个怪物获得的金币数量为 Pi

然而这个游戏允许拥有的金币数量是有上限的,当超过时,超过上限的部分就会被系统光明正大地吃掉,哲哲就拿不到了。

为了不浪费金币,哲哲决定,当下一个要击杀的怪物可获得的金币会导致自己拥有的金币数量超过上限时,就去消费一次,把自己已有的金币全部用完。

现在给定哲哲将要击杀的一系列怪物对应的金币数量,请你计算一下哲哲去消费了几次。

输入格式:

输入第一行是两个整数 N,M (1≤N≤103,1≤M≤106),表示击杀的怪物数量以及系统允许拥有金币数量的上限。

接下来一行是由空格隔开的 N 个数 Pii=1,⋯,N),依次表示击杀第 i 个怪物能获得的金币数量。假设哲哲是按输入顺序击杀怪物的,并且每个 Pi 都是 不超过 106 的非负整数。

输出格式:

在一行中输出哲哲去消费的次数。

输入样例:

10 10
1 2 3 4 1 2 3 5 11 1

输出样例:

4

样例解释:

消费时间点为:第四个怪物击杀后、第七个怪物击杀后、第八个怪物击杀后、第九个怪物击杀后。

代码长度限制 16 KB

时间限制 400 ms

内存限制 64 MB

思路:

如果加上当前的金币小于等于m,就加上;否则需要购物一次,再加上当前的金币。

代码:

#include <bits/stdc++.h>

using namespace std;

int a[1010];

int main()

{

int n, m;

cin >> n >> m;

int s = 0, res = 0;

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

cin >> a[i];

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

{

if (a[i] + s <= m)

s += a[i];

else

{

res++;

s = a[i];

}

}

cout << res << endl;

}

RC-u2 智能服药助手 分数 15

智能看护中很重要的环节是安排需要服药的老年人的服药计划。

已知机器人需要照顾的某位老年人需要服用 N 种药物,但某些药物不宜间隔过短服用 —— 比如降糖药一般遵医嘱日服 3 次,两次之间需要间隔至少 4 小时。当需要服用的药物比较多,医嘱比较复杂时,如何保证每位老人的服药计划是安全合理的,就成为一个挑战。

本题给定一套服药计划,请你检查一下计划是否存在问题。

输入格式:

输入第一行给出两个整数 N,M(1≤N,M≤103),表示老人需要服用 N 种药物(药物种类从 1 到 N 编号),对应的服药计划有 M 条记录。

接下来首先在一行中给出 N 个用空格隔开的整数 Ti (−1≤T**i≤100,T**i\=0),表示编号为 i 的药物需要间隔至少 Ti 个单位时间服用。如果 Ti 为 −1,则说明这种药物没有间隔要求。

接下来的 M 行,每行给出一条服药计划中的记录,格式为:首先给出两个非负整数 tk (0≤t≤109,0≤kN),表示服药的时刻为 t,服用了 k 种药物;然后紧接着列出 k 个数,每个数对应 t 时刻要吃的药物种类的编号。一行中的数字之间以空格分隔。

题目保证:记录按照服药时刻 t 的递增顺序给出;每一时刻服用的药物种类各不相同。注意:同一种药物可能需要在不同的时刻重复服用。如果一位老人在 ti 时刻和 tj 时刻服用了同一种药物,则他服用的间隔时间为 ∣titj∣。

输出格式:

按照输入顺序检查每一条记录中的每一种药物。如果在 Y 时刻不宜服用药物 X,则在一行中输出:

Don't take X at Y!

注意:老人收到提醒后会按照提醒不服用指定的药物。

输入样例:

10 6
1 2 3 4 5 -1 -1 -1 -1 -1
0 1 1
1 2 1 2
2 1 2
3 2 1 3
5 3 1 3 4
6 2 1 4

输出样例:

Don't take 2 at 2!
Don't take 3 at 5!
Don't take 4 at 6!

代码长度限制16 KB

Java (javac)

时间限制 1200 ms

内存限制 512 MB

其他编译器

时间限制 400 ms

内存限制 64 MB

思路:

记录上次吃药时间,吃药时和上次时间对比。

代码:

#include <bits/stdc++.h>

using namespace std;

int a[1010]; // 记录药物i的间隔时间

map<int, int> mp; // 记录药物i上次吃的时间

int main()

{

int n, m;

cin >> n >> m;

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

{

cin >> a[i];

if (a[i] == -1)

a[i] = 0;

}

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

{

int t, k;

cin >> t >> k;

t++; // 从1开始计时

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

{

int x;

cin >> x; // 药物编号

// cout<<"here: "<<t<<' '<<x<<' '<<mp[x]<<endl;//输出药物编号和吃药时间

if (mp[x] == 0)

mp[x] = t;

else if (t - mp[x] >= a[x])

mp[x] = t;

else

printf("Don't take %d at %d!\n", x, t - 1);

}

}

}

RC-u3 跑团机器人 分数 20

在桌面角色扮演游戏(TRPG,俗称“跑团”)中,玩家需要掷出若干个骰子,根据掷出的结果推进游戏进度。在线上同样可以跑团,方法是由玩家们向机器人发出指令,由机器人随机产生每个需要掷出的骰子的结果。

玩家向机器人发出的指令是一个仅涉及加法和减法的表达式,即对若干个数字进行一系列加法或减法计算。这些数字可以是直接给出的非负整数(数字不超过 1000),也可以是若干个骰子掷出的结果。

“掷骰子”这个动作对应的指令格式为 xdy,表示摇动 xy 面的骰子(1≤x≤1000,2≤y≤1000)。当 x 为 1 时,1 可以省略。

例如指令 2d3+3-d4 的意思是:先掷出 2 个 3 面骰子(你不必考虑现实中是否存在这样的骰子),不妨假设结果为 1 和 3,则 2d3 的结果就是两个骰子的面值之和 4;然后计算 4 + 3,得到结果为 7;再掷出 1 个 4 面骰子,不妨假设结果为 2,则计算 7 - 2 得到最终结果 5。

本题就请你计算玩家输入的指令里,不同种类的骰子需要掷出几个,以及可能得到的结果在什么区间范围内。

输入格式:

输入在一行中给出一条符合题目描述的玩家输入机器人的指令。题目保证指令长度不超过 2∗104。

输出格式:

首先输出不同种类的骰子分别需要掷出几个。每种骰子的信息占一行,依次输出骰子的面数和投掷的数量,按面数从小到大输出。

输入指令保证至少有一个骰子需要掷出。

最后一行输出两个数,表示根据输入指令可以得到的最小结果和最大结果。

同一行数字间以 1 个空格分隔,行首尾不得有多余空格。

输入样例:

d6+3d5+2-2d3+2d5

输出样例:

3 2
5 5
6 1
2 31

代码长度限制16 KB

时间限制400 ms

内存限制64 MB

思路:

没拿满,不知道哪里错了只拿到了16

代码:

import re

def f(x):

if (x == '+'):

return 1

else:

return -1

s = input()

sign = re.findall(r'[+-]', s)

data = re.split(r'[+-]', s)

# print(sign,data)

l = r = 0

d = {}

for i in range(len(data)):

if (len(data) != len(sign)):

if (i == 0):

sg = 1

else:

sg = f(sign[i-1])

else:

sg = f(sign[i])

if 'd' in data[i]:

t = data[i].split('d')

if (t[0] == ''):

x = 1

y = int(t[1])

else:

x = int(t[0])

y = int(t[1])

if (sg == 1):

l += sg*x

r += sg*y*x

else:

l += sg*x*y

r += sg*x

d[y] = d.get(y, 0)+x

else: # 数字

l += sg*int(data[i])

r += sg*int(data[i])

# print(x,y,sg,l,r)

res = list(d.items())

res.sort()

for i in res:

print(i[0], i[1])

print(l, r)

RC-u5 树与二分图 分数 30

G=(V,E) 是一个无向图,如果顶点集合 V 可分割为两个互不相交的子集 (A,B),并且每条边 (i,j)∈E 的两个端点 ij 分别属于这两个不同的顶点子集,则称图 G 为一个二分图。

现在给定一棵树 T,要求选择树中两个没有边相连的结点 ij,使得将无向边 (i,j) 加进 T 后能够构成二分图。你的任务是计算满足这个要求的选择方案有多少种。

输入格式:

输入第一行给出一个正整数 N (2≤N≤106),表示树中结点的个数。

接下来 N−1 行,每行给出树中一条边的两端结点编号,以空格分隔。结点编号从 1 开始。题目保证输入给出的是一棵树中所有的边。

输出格式:

在一行中输出方案数。注意:连接 (1,2) 和 (2,1) 视作同一个方案。

输入样例:

7
1 2
2 3
2 4
2 5
2 6
4 7

输出样例:

4

代码长度限制16 KB

Java (javac)

时间限制1300 ms

内存限制512 MB

其他编译器

时间限制500 ms

内存限制64 MB

思路:

将图划分为两个子图,统计两个子图的顶个个数。

代码:

#include <bits/stdc++.h>

using namespace std;

#define N 1000010

#define int long long

// 找两个两个图的顶点个数a b

// 所求即为最大边数(a*b)-当前边数

vector<int> g[N];

int vis[N];

int n1, n2;

void dfs(int u, int dep)

{

if (dep % 2)

n1++;

else

n2++;

vis[u] = 1;

for (auto i : g[u])

if (!vis[i])

dfs(i, dep + 1);

}

signed main()

{

int n;

cin >> n;

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

{

int x, y;

cin >> x >> y;

g[x].push_back(y); // 无向图

g[y].push_back(x);

}

dfs(1, 0);

// cout<<n1<<' '<<n2<<endl;

cout << n1 * n2 - (n - 1) << endl;

}