2021CCPC女生赛

1 篇文章 0 订阅

2021CCPC女生赛


距离女生赛过去已经一个月了,竟然没有补题和整理过。每天要看论文做实验还要准备一个又一个的考试,时间总是不够用的,状态还是依然的差。


现场大概5题银首到铜尾了。
金牌的话要做出更多更多的题目才行。
因为错读了C题,浪费了很多时间,最后写模拟就很晚了,最气人的是封榜以后刚好写完,在本地测试样例一直是通过的,一交到PTA测试样例竟然是WA!!?,调了好久,后来学妹说就是那个方向函数有点问题,但是我真不知道出了啥问题,最后乱改一通,发现是
在这里插入图片描述
里面的if如果不成立就会返回奇怪的值!!于是改掉了就一下子顺利通过了!

A.公交线路

  • 题目大意:
    有一条有n个站点的道路,有一个双向运行的公交车,每到一个站点会播报站点名字,小Q现在要从公司回家,小Q家为x站点,小Q公司为y站点,他只能听清楚站点的名字长度,现在给你m个公交车经过的站点的名字长度,问:小Q是否行驶在回家的正确方向上?
    输出“Right”,“Wrong”,“Unsure”。
  • 思路:
    模拟!先判断从公司到家的方向是数组的正向还是逆向,调整数组。
    然后一点点的判断n个位置是否与m长的序列对应。
  • Code:
#include<bits/stdc++.h>
using namespace std;
int n,x,y,m;
int shu[105],id[105];
int main(){
	cin>>n>>x>>y;
	for(int i=1;i<=n;i++)cin>>shu[i];
	cin>>m;
	for(int i=1;i<=m;i++)cin>>id[i];
	int flagl=1,flagr=1;
	for(int i=x+1;i<=x+m;i++) 
	{
		if(shu[i]!=id[i-x]) flagr=0;
	}
	for(int i=x-1;i>=x-m;i--) 
	{
		if(shu[i]!=id[x-i]) flagl=0;
	}
	if(flagl==1&&flagr==1)cout<<"Unsure"<<endl;
	else if(flagl==1&&y<x)cout<<"Right"<<endl;
	else if(flagr==1&&x<y)cout<<"Right"<<endl;
	else cout<<"Wrong"<<endl;
	return 0;
}

D.修建道路

  • 题目大意:
    比特镇有 n 个村庄,编号依次为 1 到 n。现在需要修建 n−1 条双向道路将这些村庄连通起来,每条道路的端点只能是这 n 个村庄之一。准确地说,如果要修建一条直接连接村庄 i 与村庄 j 的双向道路 (1≤i≤j≤n),那么需要支付 maxi≤k≤j{ak} 的费用。
    请写一个程序,找到一个总费用最小的修路方案,使得任意两个村庄都能通过这些道路直接或间接到达。
    Input
    第一行包含一个正整数 n (1≤n≤200000),表示村庄的数量。
    第二行包含 n 个正整数 a1,a2,…,an (1≤ai≤109)。
    Output
    输出一行一个整数,即所需的最小总费用。

  • 思路:
    考虑最后一个点要跟前面的点连通,需要在前面任意找到一点连边,这条边的权值取决于与他连边的点经过的最大值,然而这个值最好的情况就是他与他前面的点相连,对于每个点都是如此,因此只要所有点都和他前面的点相连统计一遍答案即可。
    要开long long!!!

  • Code:

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

long long n, a[N], b[N], ans=0;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		b[i]=max(a[i-1],a[i]);
	} 
	int maxn=a[n];
	for(int i=2;i<=n;i++)
	{
		ans+=b[i];
	}
	cout<<ans<<endl;
	return 0;
}

G.3G网络

  • 题目大意:
    比特镇建镇多年一直没有通网,工程师小C为了改善比特镇人民的生活,立下了宏伟的目标,致力于比特镇3G网络全域覆盖的实现。
    比特镇可以被视为一个充分大的二维平面,工程师小C敲定了 n 个建立3G网络基站的位置,每个基站能够实现以基站为圆心的半径为 r 的圆内区域的3G网络覆盖。
    现在工程师小C想知道,当 r 足够大以至于比特镇的每一个角落都有3G网络覆盖时,比特镇3G网络覆盖范围的面积与这 n 个基站的3G网络覆盖范围的面积之和的比值。
    更形式化地描述这个问题,记 n 个3G网络基站的位置分别为 (x1,y1),(x2,y2),…,(xn,yn),定义 Ci,r={(x,y)∈R2∣(x−xi)2+(y−yi)2≤r2} 为第 i 个3G网络基站覆盖的范围,你需要计算
    f ( r ) = S ( C 1 , r ∪ C 2 , r ∪ … ∪ C n , r ) S ( C 1 , r ) + S ( C 2 , r ) + … + S ( C n , r ) f(r)= \frac{S(C1,r∪C2,r∪…∪Cn,r)}{S(C1,r)+S(C2,r)+…+S(Cn,r)} f(r)=S(C1,r)+S(C2,r)++S(Cn,r)S(C1,rC2,rCn,r)
    r → + ∞ {r→+∞} r+ f ( r ) f(r) f(r) 的极限 lim ⁡ r → + ∞ f ( r ) {\lim_{r \to +\infty} f(r)} limr+f(r),其中 S(X) 表示平面点集 X 的面积。
    Input
    第一行包含一个正整数 n (1≤n≤2000),表示3G网络基站的个数。
    接下来 n 行,每行包含两个整数 x,y (−10000≤x,y≤10000),表示3G网络基站建立的位置,保证任意两个3G网络基站都不建在同一处。
    Output
    输出一行,包含一个实数表示 lim ⁡ r → + ∞ f ( r ) {\lim_{r \to +\infty} f(r)} limr+f(r),要求绝对误差不超过 1 0 − 9 10^{−9} 109。也就是说,如果你给出的答案是 a,标程给出的答案是 b,你的答案被认为是正确的当且仅当 ∣ a − b ∣ ≤ 1 0 − 9 |a−b|≤10^{−9} ab109
    样例
    input
    1
    0 0
    output
    1.000000000000000

  • 思路:
    观察样例,发现是 1 n \frac{1}{n} n1

  • Code:

#include<bits/stdc++.h>
using namespace std;

int n,x,y;

int main()
{
	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d%d",&x,&y);
	
	printf("%.15lf\n",(1.0)/(double)n);
	
	return 0;
}

I.驾驶卡丁车

  • 题目大意:
    小Q正在设计一款2D卡丁车游戏,你的任务是帮助小Q实现其中的一部分功能。
    在这款游戏中,游戏地图是一张 n 行 m 列的网格,从上到下依次编号为第 1 行到第 n 行,从左往右依次编号为第 1 列到第 m 列,其中第 i 行第 j 列的格子的坐标为 (i,j),每个格子要么是可以通行的平地,要么是不可通行的障碍。
    在地图上的某个平地格子处有一辆由玩家操控的卡丁车。卡丁车的移动速率为 v,并且一共有8种可能的朝向,分别为:
  • “上”:前进一步时,将从 (x,y) 移动到 (x−1,y)。
  • “下”:前进一步时,将从 (x,y) 移动到 (x+1,y)。
  • “左”:前进一步时,将从 (x,y) 移动到 (x,y−1)。
  • “右”:前进一步时,将从 (x,y) 移动到 (x,y+1)。
  • “左上”:前进一步时,将从 (x,y) 移动到 (x−1,y−1)。
  • “右上”:前进一步时,将从 (x,y) 移动到 (x−1,y+1)。
  • “左下”:前进一步时,将从 (x,y) 移动到 (x+1,y−1)。
  • “右下”:前进一步时,将从 (x,y) 移动到 (x+1,y+1)。
    一开始卡丁车朝上位于某个平地格子处,其初始移动速率为 v=0。接下来玩家将依次输入 q 条操作指令,每条操作指令是下列中的一种:
  • “L”:卡丁车朝向往左转 45 度。
  • “R”:卡丁车朝向往右转 45 度。
  • “U”:卡丁车的速率由 v 增大至 v+1。
  • “D”:卡丁车的速率由 v 减小至 max(v−1,0)。
    在执行完每条操作指令后,卡丁车都会沿着其朝向前进 v 步,在移动结束后才会继续响应后续指令。在前进的过程中,如果某一步尝试驶入某个障碍格子或者尝试驶出地图,那么说明卡丁车发生了碰撞,它将就此结束移动,在保持朝向的同时速率 v 降低为 0。特别要注意的是,当朝向是斜45度时,为了防止"穿模"现象的发生,如果卡丁车两侧都是障碍,那么卡丁车同样将被认为发生了碰撞。例如卡丁车朝向右下,现在将从 (x,y) 移动到 (x+1,y+1),那么如果 (x+1,y) 和 (x,y+1) 都是障碍,则卡丁车发生了碰撞。
    请写一个程序,在执行完每条操作指令后且卡丁车完成移动之后,汇报卡丁车的坐标以及这次移动过程中是否发生了碰撞。

Input
第一行包含两个正整数 n 和 m (1≤n,m≤50),表示地图的尺寸。
接下来 n 行,第 i 行包含一个长度为 m 的字符串,其中第 j 个字符描述格子 (i,j)。如果它是".“,则说明 (i,j) 是平地;如果它是”#“,则说明 (i,j) 是障碍;如果它是”“,则说明 (i,j) 是平地,且卡丁车位于此。输入数据保证存在恰好一个”“。
接下来一行包含一个正整数 q (1≤q≤500),表示指令的数量。
接下来一行包含一个长度为 q 的字符串,每个字符是四种指令中的一种,依次描述每条指令。
Output
输出 q 行,第 i 行输出执行完第 i 条指令且卡丁车完成移动之后的相关信息。如果这一次卡丁车没有发生碰撞,那么输出"x y”;如果这一次卡丁车发生了碰撞,那么输出"Crash! x y"。其中 x 和 y 表示卡丁车的坐标为 (x,y)。

  • 思路:
    模拟!!比赛时写了好久呜呜呜,太久没好好写代码了。
    写模拟:掌握基本的表达技巧+好好读题+理清框架慢慢写+把功能函数化
    现场过了大模拟让自己信心满满起来了!
  • Code:
#include<bits/stdc++.h>
using namespace std;

const int N = 55;

int n,m,q,x,y;
string s;
int mp[N][N];
struct node{
	int x,y;
};
struct node path[8]={{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1}};

int check(int dir)
{
	if(dir==1 || dir==3 || dir==5 || dir==7) 
	{
		if((mp[x+path[(dir+7)%8].x][y+path[(dir+7)%8].y]==1) && (mp[x+path[(dir+1)%8].x][y+path[(dir+1)%8].y]==1))
			return 1;
	}
	return 0;
}

int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>s;
		for(int j=0;j<m;j++)
		{
			if(s[j]=='.') mp[i][j+1]=0;
			else if(s[j]=='#') mp[i][j+1]=1;
			else
			{
				mp[i][j+1]=0;
				x=i,y=j+1;
			}
		}	
	} 
	
	cin>>q;cin>>s;
	int v=0,dir=0;

	for(int step=0;step<q;step++)
	{

		bool is_ok=true;
		if(s[step]=='L') 
		{
			dir=(dir+7)%8;	
		}
		else if(s[step]=='R')
		{
			dir=(dir+1)%8;
		} 
		else if(s[step]=='U')
		{
			v++;	
		} 
		else if(s[step]=='D')
		{
			v=max(v-1,0);
		}
		
		int xx,yy;
		//cout<<"v"<<" "<<v<<endl;
		for(int i=1;i<=v;i++)
		{	
			yy=y+(path[dir].y);
			xx=x+(path[dir].x);
			if(mp[xx][yy]==1||xx<=0||yy<=0||xx>n||yy>m||check(dir))
			{
				printf("Crash! %d %d\n",x,y);	
				v=0;
				is_ok=false;
				break;
			}
			
			x=xx;y=yy;
				
		}
		if(is_ok)
		{
			printf("%d %d\n",x,y);
		}
	}

	
	return 0;
}

K. 音乐游戏

  • 题目大意:
    小Q最近一直在练习osu!mania的4键下落式模式,现在给出一张4键下落式模式的谱面,你需要帮他计算这张谱面中有多少个音符。
    Input
    第一行包含一个正整数 n (10≤n≤1000),表示给出的4键下落式模式谱面的长度。
    接下来 n 行,每行包含一个长为6的字符串,保证第一个和最后一个字符是单个竖线(‘|’),表示谱面展示区的两侧边界,其余4个字符要么是单个空格(’ ‘),表示对应位置没有音符,要么是单个短横线(’-'),表示对应位置有一个音符。
    Output
    输出一行,包含一个整数,表示谱面中的音符数量。
    input
    10
    |- |
    | —|
    |- |
    | - |
    | - |
    |- --|
    | - |
    |- -|
    | - |
    |-- -|
    output
    17
  • 思路:
    第一个签到题
    好笑的是,一开始紧张连读字符写错了好几次才改对的hhh。
  • Code:
#include<bits/stdc++.h>
using namespace std;

int n;
char s[10];
char c;
int main()
{
	cin>>n;
	int cnt=0;
	getchar();
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<6;j++)
		{
			c=getchar();
			
			if(c=='-') cnt++;
		}
		getchar();
	}
	cout<<cnt<<endl;
	
	return 0;
}

下面是补题了

C. 连锁商店

  • 题目大意:
    有n家商店,m条索道(只有从编号小的向编号的大的单向索道),每家商店隶属于一家公司,每家公司会有一个红包给游客。只能在这家公司下属一家商店领一次这家公司的红包,问从一号商店到每一个商店最多能收获多少红包。

  • 思路:
    比赛的时候一直在写这题没过耽误了很多时间,可惜。
    对于每一个点只需记录他所隶属的商店红包是否被领取过,贪心的往后dfs即可。
    需要注意的是:边数过大,需要优化边数:对于像1->2->3->4和1->4这两条到达4的路线,显然前者优于后者,因此考虑使用floyd优化点短路径。

  • Code:

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

int n,m,w[N],com[N],money[N],u,v,st[N],mp[N][N];
vector<int> e[N];
int ans[N];

void dfs(int now,int sum)
{
	ans[now]=max(ans[now],sum);
	for(auto i: e[now])
	{
		if(st[com[i]]==0) 
		{
			st[com[i]]=1;
			dfs(i,sum+money[com[i]]);
			st[com[i]]=0;
		}
		else 
		{
			dfs(i,sum);
		}
	}
	
}

int main()
{
	
	cin>>n>>m;
	for(int i=1;i<=n;i++) scanf("%d",&com[i]);
	for(int i=1;i<=n;i++) scanf("%d",&money[i]);	

	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		mp[u][v]=1;
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			for(int k=j+1;k<=n;k++)
				if(mp[j][k]&&mp[j][i]&&mp[i][k])
					mp[j][k]=0;
	
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
			if(mp[i][j])
				e[i].push_back(j);
	ans[1]=money[com[1]];
	st[com[1]]=1;
	dfs(1,ans[1]);
	for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
	return 0;
}

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值