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

发布时间 2023-08-06 09:58:41作者: 回忆、少年

RC-u1 智能红绿灯

题目描述:
RC-u1 智能红绿灯
为了最大化通行效率同时照顾老年人穿行马路,在某养老社区前,某科技公司设置了一个智能红绿灯。

这个红绿灯是这样设计的:

  • 路的两旁设置了一个按钮,老年人希望通行马路时会按下按钮;
  • 在没有人按按钮的时候,红绿灯一直为绿灯;
  • 当红绿灯为绿灯时,有人按下按钮,第一次按下按钮的 15 秒后绿灯会转红;
  • 转红后,红灯会持续 30 秒,方便老年人穿行马路;
  • 在 30 秒的红灯期间,假如有人再次按下按钮,则红灯会再延续 15 秒;
  • 延续一次后不会再次延续。

现在给定按钮被按下的时间点,请你输出这个智能红绿灯的红灯时间区间。

注意:我们假设同一秒内,红绿灯先变化,然后按钮再被按下。每 1 秒理解为一个时间点。例如:在第 1 秒按下按钮,则第 16 秒开始变红;如果没有人在第 16 - 45 秒这个闭区间内按下按钮,则到第 46 秒开始变绿。而在第 46 秒按下按钮的人,需要等 15 秒后才有红灯。

输入格式:
输入第一行为 N (1≤N≤1000),表示按钮被按下的次数。

接下来一行 N 个非负整数,表示按钮被按下的时间点。一个时间点按钮有可能会被多次按下,给出的时间点保证是不递减的。

时间点的范围不超过 10^4。

输出格式:
输出若干行,按起始时间从小到大输出互不相交的红灯的时间区间。

输入样例:

10
3 4 5 6 33 45 49 70 90 100

输出样例:

18 62
85 129

代码实现:

#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int N=1005;
int a[N];
signed main(){
    int n;
    cin>>n;
    for(int i=0;i<n;i++)cin>>a[i];
    int bg=a[0]+15,ed=bg+29,flag=0;
    for(int i=1;i<n;i++){
        if(a[i]<=ed&&a[i]>=bg){
            if(!flag)ed+=15;
            flag=1;
        }
        else{
            if(a[i]>ed){
                cout<<bg<<" "<<ed<<endl;
                bg=a[i]+15,ed=bg+29,flag=0;
            }
        }
    }
    cout<<bg<<" "<<ed<<endl;
    return 0;
}

RC-u2 女王的大敕令

题目描述:
女王的大敕令
副本是游戏里的一个特色玩法,主要为玩家带来装备、道具、游戏资源的产出,满足玩家的游戏进程。

在 MMORPG《最终幻想14》里,有一个攻略人数最大达到 48 人的副本“零式贡希尔德神庙”,其中守关 BOSS “天佑女王”有一个很有趣的技能:“女王的大敕令”。

技能在一个 5×5 的棋盘上展开。每位玩家根据给定的两个步长,从某个方格出发,在棋盘上先走 D1步,再走 D2步。其中“步长”指的是曼哈顿距离,即:设两个方格的坐标分别为 (Xi,Y i) 以及 (Xj,Yj),则这两个方格的曼哈顿距离 D=∣Xi−X j∣+∣Yi−Y j∣。

例如下图中的 A 点与 B 点的曼哈顿距离为 5:
image

技能开始时,场地外围会出现 4 只小怪,东南西北(即棋盘的右、下、左、上)方向各出现一只小怪,且小怪一定出现在某行或某列对应的位置上。第 i 只小怪会顺时针朝固定方向移动 ni步(题目保证不会移出界,即移动后仍然在对应着某行/某列的位置上),且:

  • 北边的小怪固定向右移动;
  • 东边的小怪固定向下移动;
  • 南边的小怪固定向左移动;
  • 西边的小怪固定向上移动。

小怪出现后,棋盘上还会出现一个发光的格子,这是玩家移动的目标点,如图所示:
image
玩家必须在不被小怪杀死的前提下,按规定步长,用两个回合到达目标点。技能流程如下:

1、玩家先选择一个起始方格;
2、东、西两侧的小怪开始按照固定方向移动,移动完毕后 4 只小怪会同时开展攻击,其中东、西两侧的小怪攻击自己所对应的一整行,南、北两侧的小怪攻击自己所对应的一整列。玩家若处在攻击区内则任务失败。
3、玩家移动 D1步,到达某个方格;
4、南、北两侧的小怪开始按照固定方向移动,移动完毕后 4 只小怪会同时开展攻击,同第 2 步;
5、玩家移动 D2步,此时必须到达目标点,否则任务失败。
以下是上面的 4 只小怪都移动后的攻击范围的示意图:
image
给定小怪起始位置以及移动步数 ni和目标点位置,请输出所有安全的移动方案,包括起始点以及第一次移动的目的地。

输入格式:
输入第一行是四个数 C1,C2,R1,R2,分别表示:

  • 北边(上面)的小怪 1 在第 C1列的位置上;
  • 南边(下面)的小怪 2 在第 C2列的位置上;
  • 西边(左边)的小怪 3 在第 R1行的位置上;
  • 东边(右边)的小怪 4 在第 R2行的位置上。

输入第二行是四个数 ni(i=1,⋯,4),按照上面的顺序给出小怪移动的步数,保证小怪移动后仍然处于某行或某列对应的位置上。

输入第三行是四个数 row,col,D1,D2,依次表示目标点的位置,以及玩家要走的两个步长。这里某方格的“位置” (row,col) 指的是该方格的行号、列号组成的二元组。

我们假设左上角的方格位置为 (1, 1)。

输出格式:
输出安全移动的方案,方案由两个位置共四个数组成,前两个数为初始选择的方格的位置,后两个数为第一次停留的位置。

对于多个方案的情况,先按初始方格位置从小到大输出,初始方格相同时按第一次停留位置从小到大输出。一个坐标 (ri,ci) 比另一个坐标 (rj,cj) 小,当且仅当 ri<rj,或 ri=rj的同时有 ci<cj。

输入样例:

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

输出样例:

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

代码实现:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int N=10;
int tr[N],dir[N];
struct node{
    int x,y,l,r;
}a[105];
vector<node>v;
bool cmp(node a,node b){
    if(a.x!=b.x)return a.x<b.x;
    else if(a.y!=b.y)return a.y<b.y;
    else if(a.l!=b.l)return a.l<b.l;
    else return a.r<b.r;
}
int cal(int x1,int y1,int x2,int y2){
    return abs(x2-x1)+abs(y2-y1);
}
int main(){
    for(int i=1;i<=4;i++)cin>>dir[i];
    for(int i=1;i<=4;i++)cin>>tr[i];
    int row,col,d1,d2;
    cin>>row>>col>>d1>>d2;
    for(int i=1;i<=5;i++){
        for(int j=1;j<=5;j++){
            for(int i1=1;i1<=5;i1++){
                for(int j1=1;j1<=5;j1++){
                    if(cal(i1,j1,i,j)==d1&&cal(row,col,i1,j1)==d2){
                        int k1=min(5,dir[4]+tr[4]),k2=max(1,dir[3]-tr[3]);
                        if(i!=k2&&i!=k1&&j!=dir[1]&&j!=dir[2]){
                            int k3=min(5,dir[1]+tr[1]),k4=max(1,dir[2]-tr[2]);
                            if(i1!=k2&&i1!=k1&&j1!=k3&&j1!=k4)v.push_back({i,j,i1,j1});
                        }
                    }
                }
            }
        }
    }
    sort(v.begin(),v.end(),cmp);
    for(int i=0;i<v.size();i++)cout<<v[i].x<<" "<<v[i].y<<" "<<v[i].l<<" "<<v[i].r<<endl;
    return 0;
}

RC-u3 战利品分配

题目描述:
RC-u3 战利品分配
在某个战争游戏中,多个玩家组成一个大型军团,攻下若干城池,并获得战利品。

具体而言,游戏中有 N 个城市,并以 M 条长度为 1 的无向道路连接,玩家们组成的军团从 S 号城市开始进攻,目的地是 T 号城市,每个城市攻下后的战利品价值为 pi。

为了合理地分配战利品,军团们定下了规矩:假设军团里有 K 位玩家,那么从 S 号城市开始,第 1 个攻下的城市分配给第 1 位玩家,第 2 个攻下的分配给第 2 位玩家,……,第 K 个攻下的分配给第 K 位玩家,第 K+1 个攻下的则重新开始计算,分配给第 1 位玩家,以此类推。

军团很强,路上所有的城市都可以轻松进攻下来。你作为军团的指挥,可以指定玩家的进攻路线。但玩家们都希望尽快结束游戏,因此 S 到 T 的距离必须是最短的。你需要做的是在最短距离的限制下选择对自己最好的线路,获得尽可能高的战利品价值。请输出你的答案。

输入格式:
输入第一行是四个数 N,M,K,P (1≤N,M≤10^5, 1≤K≤10^4,1≤P≤K),表示城市数量(于是城市从 1 到 N 编号)、连接道路数量以及你在军团中的 K 位玩家中排第 P 位(即你战利品分配在第 P 位)。

第二行是 N 个被空格隔开的非负整数,第 i 个数对应 pi(0≤pi≤10^4),表示编号为 i 的城市的战利品价值(i=1,⋯,N)。

然后的 M 行,每行给出两个用空格分隔的正整数 U 和 V,表示编号为 U 和 V 的城市之间有道路连接。

最后的一行是两个正整数 S,T,表示开始的城市编号与目的地的城市编号。开始和目的地的城市也是可以进攻并获取战利品的。

输出格式:
输出一行,表示你可以取得的最大价值。

输入样例:

9 11 2 2
100 150 130 50 30 20 200 0 70
1 2
1 3
2 3
2 4
2 5
3 6
4 7
5 7
6 8
7 9
8 9
1 9

输出样例:

350

代码实现:

#include<iostream>
#include<queue>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
#define int long long
typedef pair<int,int>PII;
const int N=1e5+5,M=2*N;
int h[N],e[M],ne[M],idx;
int dist[N],vis[N],val[N],S,T,res[N],k,p;
void add(int x,int y){
	e[idx]=y,ne[idx]=h[x],h[x]=idx++;
}
void dijkstra(int x){
	priority_queue<PII,vector<PII>,greater<PII>>q;
	memset(dist,0x3f,sizeof dist);
	dist[x]=0;
	q.push({0,x});
	while(q.size()){
		int s=q.top().second;
		q.pop();
		if(vis[s])continue;
		vis[s]=1;
		for(int i=h[s];~i;i=ne[i]){
			int j=e[i];
			if(dist[j]>dist[s]+1){
				dist[j]=dist[s]+1;
				if((dist[j]+1)%k==p%k)res[j]=res[s]+val[j];
				else res[j]=res[s];
				q.push({dist[j],j});
			}else if(dist[j]==dist[s]+1){
				if((dist[j]+1)%k==p%k){
					if(res[s]+val[j]>res[j]){
						res[j]=res[s]+val[j];
					}
				}else res[j]=max(res[s],res[j]);
			}
		}
	}
}
signed main(){
	int n,m;
	cin>>n>>m>>k>>p;
	for(int i=1;i<=n;i++)cin>>val[i];
	memset(h,-1,sizeof h);
	while(m--){
		int x,y;
		cin>>x>>y;
		add(x,y);
		add(y,x);
	}
	cin>>S>>T;
	dijkstra(S);
    //dijkstra过程并没有对第一个点进行判断,故第一个点需要特判
    if(p==1)res[T]+=val[S];
	cout<<res[T]<<endl;
	return 0;
}

RC-u5 养老社区

题目描述:
RC-u5 养老社区
作为智能看护的一部分,你需要评估某个养老社区是否适合开展智能看护的服务。

这个养老社区有若干幢住宅楼,每个住宅楼有一个种类,住宅楼之间由长度为 1 的道路连接,道路都是双向道路且没有构成环 —— 你可以简单地认为养老社区的路构成了一棵树。

假设我们能找到三个住宅楼,这三个住宅楼两两之间的最短距离相等,并且三个住宅楼的种类不一样,那么我们称这三个住宅楼组成的三元组为适合智能看护的,指的是为了服务这三个住宅楼,我们可能可以方便地找到适合建设服务中心的地方。一个社区的适合度指的是能够找到多少本质不同的适合智能看护的住宅楼三元组。

本质不同两个的三元组指的是:三元组内元素任意排列后,两个三元组仍然不相等。

给定这个养老社区的情况,请你求出这个社区的适合度。

输入格式:
输入第一行是一个正整数 N (1≤N≤2×10^3),表示养老社区里住宅楼的数量(于是住宅楼从 1 到 N 编号)。

接下来 N−1 行,每行给出空格分隔的两个正整数 U 和 V,表示编号为 U 和 V 的住宅楼之间有一条长度为 1 的道路。
最后一行给出 N 个数,第 i 个数表示编号为 i 的住宅楼的种类为 Ti(1≤Ti≤N)。

保证给定的数据会将所有住宅楼连接成一棵完整的树。

输出格式:
输出一行一个正整数,表示社区的适合度。

输入样例:

11
1 2
1 3
1 4
2 5
2 6
3 7
3 8
4 9
4 10
1 11
1 2 3 4 5 6 7 8 9 9 10

输出样例:

14

代码实现:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2020;

vector<int> G[maxn];
int n, t[maxn];

int vis[maxn], e[maxn][maxn];
void bfs(int x){
    queue<int> q;
    q.push(x);
    vis[x] = 1;
    while(q.size()){
        int u = q.front();
        q.pop();
        for(int to : G[u]){
            if(vis[to])continue;
            vis[to] = 1;
            e[x][to] = e[x][u]+1;
            q.push(to);
        }
    }
}

int main(){
    cin >> n;
    for(int i = 1; i < n; i++){
        int u ,v ; 
        cin>>u>>v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    for(int i = 1; i <= n; i++) cin >> t[i];
    // bfs求任意2节点距离
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n; j++) vis[j] = 0;
        bfs(i);
    }
    // 枚举3个节点
    int cnt = 0;
    for(int i = 1; i <= n; i++){
        for(int j = i+1; j <= n; j++){
            for(int k = j+1; k <= n; k++){
            	// 判断距离 注意由于数据量 这里的条件不能交换
                if(e[i][j]==e[j][k] && e[i][j]==e[i][k]){
                	// 判断类型
                    if(t[i]!=t[j] && t[i]!=t[k] && t[j]!=t[k]){
                        cnt++;
                    }
                }
            }
        }
    }
    cout << cnt << endl;
    return 0;
}