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,r∪C2,r∪…∪Cn,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} 10−9。也就是说,如果你给出的答案是 a,标程给出的答案是 b,你的答案被认为是正确的当且仅当 ∣ a − b ∣ ≤ 1 0 − 9 |a−b|≤10^{−9} ∣a−b∣≤10−9 。
样例
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;
}