2023Robocom省赛(本科组)

RC-u1 亚运奖牌榜

题目链接:PTA | 程序设计类实验辅助教学平台 (pintia.cn)

题目:

2022 年第 19 届亚运会即将在杭州召开,杭州已经做好准备欢迎全亚洲的观众一同参与亚运盛会了!

你正在开发一款跟亚运奖牌计算相关的 App。给定两个国家的获奖情况,你的任务是计算这两个国家/地区的奖牌情况,并确定哪个国家/地区要排在奖牌榜的前面。

输入格式:

输入第一行是一个正整数 N (1≤N≤1000),表示总共有 N 条获奖记录。

接下来的每一行都是形如以下的一条记录:

Ci​,Pi​

其中 Ci​=0,1,0 表示是第一个国家/地区,1 表示是第二个国家/地区;Pi​=1,2,3,1 表示金牌,2 表示银牌,3 表示铜牌。

输出格式:

首先输出两行,第一行是第一个国家/地区的金牌、银牌、铜牌获得数,用空格隔开;第二行是第二个国家/地区的奖牌获奖情况,要求与格式同第一个国家/地区。

最后一行,如果是第一个国家/地区排在前面,输出 The first win!,否则输出 The second win!

排在前面的定义是:先比较金牌数,金牌数较大的排在前面;如金牌数相等,比较银牌数,银牌数较大的在前面;如金牌银牌数都相等,则比较铜牌数,铜牌数较大的在前面。

保证数据不存在奖牌数完全相同的情况。

样例输入:

15
0 1
0 2
0 3
0 1
0 1
0 2
0 3
1 3
1 3
1 3
1 3
1 2
1 1
1 1
1 1

样例输出:

3 2 2
3 1 4
The first win!

分析:这是一道小型模拟题,先读入两个国家的金银铜牌情况,然后按照题意中所描述的那样进行比较即可,细节见代码:

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int main()
{
	int n;
	cin>>n;
	int a[2][5]={0};
	for(int i=1;i<=n;i++)
	{
		int c,p;
		cin>>c>>p;
		a[c][p]++;
	}
	for(int i=0;i<=1;i++)
	{
		for(int j=1;j<=3;j++)
		{
			printf("%d",a[i][j]);
			if(j!=3) printf(" ");
		}
		puts("");
	}
	for(int i=1;i<=3;i++)
	{
		if(a[0][i]!=a[1][i])
		{
			if(a[0][i]>a[1][i])
				printf("The first win!");
			else
				printf("The second win!");
			break;
		}
	}
	return 0;
}

RC-u2 出院

题目链接:PTA | 程序设计类实验辅助教学平台 (pintia.cn)

题目:

A:最近出了一个饮料营养等级你们知道吗?例如无糖的饮料是 A 级,可乐是 D 级……

B:那……无糖可乐是什么级别?

C:AD 级吧。

A:出院!

B:出什么院,你也给我进去!

以上是某群中一段有趣的对话。请你按照里面的逻辑,在已知某些饮料的等级的情况下,给饮料定级。定级的方法是:

  • 如果是已知等级的饮料,直接输出等级;
  • 对于一个新饮料的名字,你需要将名字拆成两个已知等级的部分,然后输出这个级别。例如:Diet是A,Coke是D,那么DietCoke就是AD;
  • 如果新饮料无法拆解或者有多种拆解方法,统一定为 D 级。

输入格式:

输入第一行是两个正整数 N,M (1≤N,M≤100),表示已知的饮料有 N 种,需要定级的饮料有 M 种。

接下来首先是 N 行,每行是一个字符串和一个字符,表示一种饮料的名字和对应的等级,等级只有 A,B,C,D 四种。

然后是 M 行,每行是一个字符串,表示需要定级的饮料的名字。

所有饮料名字只包含有大小写字母,长度不超过 30,给定拥有等级的饮料的名字不会重复。

输出格式:

对于每一个需要定级的饮料,输出定好的定级。

样例输入:

5 6
Diet A
LowSugarTea B
Milk C
Coke D
Water A
DietCoke
Pepsi
Milk
CokeWater
GoodMilk
dietCoke

样例输出:

AD
D
C
DA
D
D

分析:这个就是一个字符串处理的问题,我们用map来存储饮料的等级,然后对于每一个新型饮料,我们首先判断其是否出现过,如果出现过就直接输出其等级,如果没有出现过,枚举每个位置分成两个字符串,然后分别看两个字符串的等级是否出现过,如果出现过就记录下来,如果有多种划分方式就直接定为D级就可以。最后再将这个新型饮料的级别记录一下即可,细节见代码:

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=1003;
map mp;
int main()
{
	int n,m;
	cin>>n>>m;
	string s1,s2;
	for(int i=1;i<=n;i++)
	{
		cin>>s1>>s2;
		mp[s1]=s2;
	}
	for(int i=1;i<=m;i++)
	{
		string s;
		cin>>s;
		if(mp.count(s))
		{
			cout<

RC-u3 骰子游戏

题目链接:PTA | 程序设计类实验辅助教学平台 (pintia.cn)

题目:

在某个游戏中有一个骰子游戏。在游戏中,你需要投掷 5 个标准六面骰子(骰子为一个正方体,6 个面上分别有1、2、3、4、5、6中的一个数字,骰子的质量均匀),投出的点数根据组合会获得一个“获胜等级”。获胜等级从高到低如下:

  • 五个同点数 - 五个骰子显示相同的点数
  • 四个同点数 - 四个骰子显示相同的点数
  • 葫芦 - 一对和一个三个同点数(如1、1、3、3、3)
  • 六高顺子 - 投出的点数为 2、3、4、5、6
  • 五高顺子 - 投出的点数为 1、2、3、4、5
  • 三个同点数 - 三个骰子显示相同的点数(如1、1、1、2、3)
  • 两对 - 投出的点数中有两对是相同的(如 1、1、2、2、3)
  • 一对 - 投出的点数有一对是相同的(如 1、1、2、3、4)
  • 无 - 除去以上的其他情况

给定你已经投出的一次结果,现在假设你可以选择任意个骰子重投一次,请问怎么样操作,才能最大化在重骰后获得更好的获胜等级的概率呢?

注意:更好的获胜等级需要严格地比当前的获胜等级更好,例如 1、1、2、2、3 如果重骰后变为 1、1、3、3、4 并不比当前的获胜等级更好。

输入格式:

输入第一行是一个正整数 T (1≤T≤10),表示接下来有多少组数据。
每组数据只有一行 5 个数字,表示第一次投出的 5 个骰子的点数。

输出格式:

对于每组数据输出三个整数,其中第一个整数为为了获得最大的概率需要重新骰几个骰子,后面的两个整数为重骰骰子后概率的最简分数,其中第二个整数为分子,第三个整数为分母。如果分子为 0,分母为 1。

如果有多种获得最大概率的情况,取重骰的骰子数最少的方案。

样例输入:

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

样例输出:

3 4 9
3 13 18
2 4 9

分析:这个题目由于情况数不多,直接手算各种情况即可。

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=1e5+10;
int a[10],cnt[10],m[10];
int main()
{
	int n,t;
	cin>>n;
	for(int i=0;i>t;
			cnt[t]++;
		}
		for(int j=1;j<=6;j++)
			m[cnt[j]]++;
		if(m[5]==1) cout<<0<<" "<<0<<" "<<1;
		else if(m[4]==1) cout<<1<<" "<<1<<" "<<6;
		else if(m[3]==1&&m[2]==1) cout<<2<<" "<<11<<" "<<36;
		else if(m[1]==5&&cnt[1]==0) cout<<4<<" "<<19<<" "<<324;
		else if(m[1]==5&&cnt[6]==0) cout<<1<<" "<<1<<" "<<6;
		else if(m[3]==1&&m[1]==2) cout<<2<<" "<<4<<" "<<9;
		else if(m[2]==2) cout<<3<<" "<<4<<" "<<9;
		else if(m[2]==1&&m[1]==3) cout<<3<<" "<<13<<" "<<18;
		else cout<<2<<" "<<17<<" "<<18;
		if(i!=n-1) puts("");
	}
	return 0;
}

RC-u4 相对论大师

题目链接:PTA | 程序设计类实验辅助教学平台 (pintia.cn)

题目:

在某个直播间里,观众常常会发送类似这样的弹幕:

鱼越大,鱼刺越大;鱼刺越大,肉越少;肉越少,鱼越小;所以鱼越大,鱼越小

这样通过一连串推导得出一个搞笑的结论的弹幕发送者被称为“相对论大师”。

现在给定一系列已有的推论,请你从给定的推论中挑选一些,组成一条类似于上面的弹幕,成为一名“相对论大师”。

输入格式:

输入第一行是一个正整数 N (1≤N≤1000),表示总共有多少条推论。

接下来的 N 行,每行有两对四个元素,形如下:

A 0 B 1
每对元素表示一个论点:第一个是一个长度不大于 5 的、只包含大小写字母的字符串,称为论点的核心;第二个数字固定为 0 或者 1,代表论点核心的方向属性。为简单理解,你可以将 0 理解为正面方向,1 理解为负面方向。例如:

YuCi 0 Rou 1

就可以理解为鱼刺大,肉少 。

于是一行中的两个论点就形成一条推论,表示第一个核心某个方向的属性能推出第二个核心的某个方向的属性,即鱼刺越大,肉越少

输出格式:

按照弹幕格式输出一行,例如:

Yu 0 YuCi 0 YuCi 0 Rou 1 Rou 1 Yu 1 = Yu 0 Yu 1

具体格式要求为:在一行中输出从起始论点到最终论点的所有推论,论点格式与输入相同,论点间以1个空格分隔。随后输出等号(等号前后均有1个空格),最后是相互矛盾的起始和终止论点。

如果有多种方案,选择使用推论最少的;推论条数相同的输出任意一种方案均可。

在方案中每条推论仅可使用一次。保证有解,且给定的推论中没有相同的推论。

样例输入:

5
Yu 0 Yuci 0
Rou 1 Yu 1
Yuci 0 Rou 1
Yuci 0 Gutou 0
Gutou 0 Rou 0

样例输出:

Yu 0 Yuci 0 Yuci 0 Rou 1 Rou 1 Yu 1 = Yu 0 Yu 1

分析:看一眼数据范围,只有1000,我们可以暴力来做这道题目,首先对于一个新物种,比如说A,那么我们可以将A 0设置为0节点,A 1设置为1节点,接下来又来一个B,那么就将B 0设置为2节点,B 1设置为3节点,以此类推。这样我们就可以把所有的关系用节点和边来表示了,那么现在问题就转化为求0和1,或者2和3,或者4和5,^……,或2*k和2*k+1这样的节点之间的距离,求一个最小值,在过程中记录一下路径,按照题目中要求的方式进行输出即可。细节见代码:

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=1e4+10;
bool vis[N];
vector p[N];
unordered_mapmp;
unordered_mapmpp;
int pre[N];
vector bfs(int s,int e)
{
	pre[s]=-1;pre[e]=-1;
	queue q;
	q.push(s);
	memset(vis,false,sizeof vis);
	vis[s]=true;
	while(!q.empty())
	{
		int x=q.front();
		if(x==e) break;
		q.pop();
		for(int i=0;i t;
	if(pre[e]==-1) return t;
	while(e!=s)
	{
		t.push_back(e);
		e=pre[e];
	}
	t.push_back(s);
	reverse(t.begin(),t.end());
	return t;
}
int main()
{
	int n;
	cin>>n;
	int idx=0;
	for(int i=1;i<=n;i++)
	{
		string s1,s2,id1,id2;
		cin>>s1>>id1>>s2>>id2;
		if(!mp.count(s1+" 0"))
		{
			mpp[idx]=s1+" 0";
			mp[s1+" 0"]=idx++;
			mpp[idx]=s1+" 1";
			mp[s1+" 1"]=idx++;
		}
		if(!mp.count(s2+" 0"))
		{
			mpp[idx]=s2+" 0";
			mp[s2+" 0"]=idx++;
			mpp[idx]=s2+" 1";
			mp[s2+" 1"]=idx++;
		}
		s1+=" "+id1;
		s2+=" "+id2;
		p[mp[s1]].push_back(mp[s2]);
	}
	vector ans(2002);
	for(int i=0;i s1=bfs(i,i+1);
		vector s2=bfs(i+1,i);
		if(ans.size()>s1.size()&&s1.size()>0)
			ans=s1;
		if(ans.size()>s2.size()&&s2.size()>0)
			ans=s2;
	}
	for(int i=0;i

RC-u5 相对成功与相对失败

题目链接:PTA | 程序设计类实验辅助教学平台 (pintia.cn)

题目:

注意:题面内容表达纯属娱乐,与现实无关!

网上常有人说:看 XX 只能度过一个相对成功/失败的人生。不妨假设把这个句式套用在“参加睿抗比赛“以及“玩手机游戏”上,那么有:

  • “参加睿抗比赛”必然比“不参加睿抗比赛”要成功;
  • “玩手机游戏“必然比“不玩手机游戏”要失败。

现在有 N 个人,已知这些人自己填写的是否参加了睿抗比赛以及是否玩手机游戏的情况,以及他们实际上的成功程度的排序顺序,请问最少有多少人在填写情况时说谎了?

输入格式:

输出第一行为一个正整数 T (1≤T≤5),表示数据组数。

每组数据第一行是一个正整数 N (1≤N≤105),表示总共的人数。

接下来的 N 行,第 i 行有两个数字 Ai​,Bi​,表示第 i 位参赛选手是否参加了睿抗比赛以及是否玩手机游戏,0 为没有参加/没有玩,1 为参加了/玩了。

最后一行有 N 个数,为一个选手编号 1 到 N 的排列,表示选手成功程度的排序。排序顺序从最成功到最失败。

选手编号从 1 开始。

输出格式:

对于每组数据,输出一个整数,表示最少的说谎人数。

样例输入:

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

样例输出:

0
3
2

分析:首先需要注意的一点:题目中第三个样例好像有些问题,但是主办方尚未声明,故下面做法仅为我理解基础上的做法。针对第三个样例题目中给出的解释是2和4两个人说谎,那么也就是说1,3,5三个人可以同时说真话,但是3属于不打比赛不打游戏的人,但是5属于既打比赛又打游戏的,但是题目中指出打比赛肯定比不打比赛优秀,打游戏肯定比不打游戏更失败,所以我觉得两个人之间类似于那种拓扑关系,是无法确定谁更优秀的,显然矛盾,所以我的观点就是这两个人无法同时说真话,所以我给出的答案是3.接下来给出我的做法:

先给出状态表示:

f[i][0][0]代表以到i个人结束时,最后一个人不参加睿抗比赛也不玩手机游戏的人的情况下最多说真话的人数 
f[i][0][1]代表以到i个人结束时,最后一个人不参加睿抗比赛但玩手机游戏的人的情况下最多说真话的人数 
f[i][1][0]代表以到i个人结束时,最后一个人参加睿抗比赛但不玩手机游戏的人的情况下最多说真话的人数 
f[i][1][1]代表以到i个人结束时,最后一个人参加睿抗比赛也玩手机游戏的人的情况下最多说真话的人数 

至于状态转移,首先是有f[i][j][k]=f[i-1][j][k],这是没有疑问的,接下来就对j和k的四种情况展开讨论。

j=0,k=0,这个人既不打睿抗比赛,也不玩手机游戏,则比他成功的人一定不玩手机游戏 

j=0,k=1,这个人不打睿抗比赛,但玩手机游戏,则比他成功的人可能是任何情况

j=1,k=0,这个人打睿抗比赛,但不玩手机游戏,则比他成功的人一定是打睿抗比赛而且不玩手机游戏的

j=1,k=1,这个人打睿抗比赛,玩手机游戏,则比他成功的人一定打睿抗比赛

有了这个状态转移方法后代码就很简单了,最后只需要求出前n个人四种情况下说真话的人数的最大值mx即可,那么答案就是n-mx。细节见代码:

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=1e5+10;
int f[N][2][2],a[N],b[N],c[N];
/*
f[i][0][0]代表以到i个人结束时,最后一个人不参加睿抗比赛也不玩手机游戏的人的情况下最多说真话的人数 
f[i][0][1]代表以到i个人结束时,最后一个人不参加睿抗比赛但玩手机游戏的人的情况下最多说真话的人数 
f[i][1][0]代表以到i个人结束时,最后一个人参加睿抗比赛但不玩手机游戏的人的情况下最多说真话的人数 
f[i][1][1]代表以到i个人结束时,最后一个人参加睿抗比赛也玩手机游戏的人的情况下最多说真话的人数 
*/

int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n; 
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
			scanf("%d%d",&a[i],&b[i]);
		for(int i=1;i<=n;i++)
			scanf("%d",&c[i]);
		for(int i=1;i<=n;i++)
		{
			f[i][0][0]=f[i-1][0][0];f[i][0][1]=f[i-1][0][1];f[i][1][0]=f[i-1][1][0];f[i][1][1]=f[i-1][1][1];
			if(a[c[i]]==0&&b[c[i]]==0)//这个人既不打睿抗比赛,也不玩手机游戏,则比他成功的人一定不玩手机游戏 
				f[i][0][0]=1+max(f[i-1][0][0],f[i-1][1][0]);
			else if(a[c[i]]==0&&b[c[i]]==1)//这个人不打睿抗比赛,但玩手机游戏,则比他成功的人可能是任何情况 
				f[i][0][1]=1+max(max(f[i-1][0][0],f[i-1][1][0]),max(f[i-1][0][1],f[i-1][1][1]));
			else if(a[c[i]]==1&&b[c[i]]==0)//这个人打睿抗比赛,但不玩手机游戏,则比他成功的人一定是打睿抗比赛而且不玩手机游戏的 
				f[i][1][0]=1+f[i-1][1][0];
			else//这个人打睿抗比赛,玩手机游戏,则比他成功的人一定打睿抗比赛 
				f[i][1][1]=1+max(f[i-1][1][0],f[i-1][1][1]);
		}
		int mx=max(max(f[n][0][0],f[n][0][1]),max(f[n][1][0],f[n][1][1]));
		printf("%d\n",n-mx);
		
	}
	return 0;
}

你可能感兴趣的:(算法)