【题解】CF2 合集
前言:
- 本人不会 LaTeX……请见谅
- 码风奇特,不喜勿喷哈
- 题面翻译取自 luogu,本蒟蒻也会安置原题链接
- 保证文章中不出现“显然”或者“注意到”,可能会出现“易证”
- AC 代码会放置在每一个题目的最底端,为防止 ban 码的情况出现,不设置跳转链接
- 有写错的地方欢迎各位神犇指正
- 本套题共 3 道,预计阅读 + 理解时间小于 40min
正片开始!
CF2A
题面(可从下方链接跳转看原题题面):
在 Berland 流行着纸牌游戏 Berlogging,这个游戏的赢家是根据以下规则确定的:
在每一轮中,玩家获得或失去一定数量的分数,在游戏过程中,分数被记录在 名称和得分
行中,其中名字是玩家的名字,得分是在这一轮中获得的分数。得分是负值意味着玩家失去了相应的分数。
如果在比赛结束时只有一名玩家分数最多,他就是获胜者
如果两名或两名以上的玩家在比赛结束时都有最大的分数 m,那么其中首先获得至少 m 分的玩家胜利。开始时,每个玩家都是 0 分。
保证在比赛结束时至少有一个玩家的分数为正
序言 & 结论:
难度:黄题
map 映射的熟练应用是解决此题的必要条件
推理过程:
- 对于每一个玩家,他在任意一个时刻会对应一个分数,这样的对应关系看可以考虑 map
- 可以令 map 的类型为 <string,int>,表示从名字到分数的一个映射
- 那么,对于第一种情况,直接做做完了
- 对于第二种情况,我们考虑先预处理出分数最大值,然后重新模拟加分过程
- 只要遇到合法的情况就输出并退出
细节处理:
- 存在负数,mx 的初始化要设为 -inf
- 其次注意到第二次模拟加分过程,我们考虑判断条件应设为 mp[name]>=mx (虽然 mx 是最大值,但不一定是全过程的最大值)
- 结构体也是可做的~
代码:
干就完了!
点击查看代码
#include<iostream>
#include<map>
using namespace std;
const int maxn=1e3+10;
map<string,int> mp1,mp2;
int n,a[maxn],mx=-1;
string s[maxn],res;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>s[i]>>a[i];
mp1[s[i]]+=a[i];
}
for(int i=1;i<=n;i++){
mx=max(mx,mp1[s[i]]);
}
for(int i=1;i<=n;i++){
mp2[s[i]]+=a[i];
if(mp1[s[i]]==mx&&mp2[s[i]]>=mx){
res=s[i];
break;
}
}
cout<<res<<endl;
return 0;
}
----------------------不 ~ 开 ~ 心 ~ 的分割线----------------------
CF2B
题面(可从下方链接跳转看原题题面):
给定由非负整数组成的 n×n 的正方形矩阵,你需要寻找一条路径:
- 以左上角为起点
- 每次只能向右或向下走
- 以右下角为终点
- 如果我们把沿路遇到的数进行相乘,积应当以尽可能少的 0 结尾
序言 & 结论:
本以为是个普通 dp,没曾想还给我整这死出
题面只有条件4是变种,其他都是板子
难度:绿题
推理过程:
- 如果不加入条件4,那就是一个 dp 的板子题,转移方程形如:
dp[i][j]=dp[i-1][j]+dp[i][j-1] - 那么,我们在这个板子的基础上处理条件4
- 首先,考虑转化一下“以尽可能少的 0 结尾”
- 由题意,答案是由路径上的乘积构成的……
- 末尾出现 0,说明因子必含有 0/10
- 如若含 0,乘积是 0,末尾有一个 0
- 对于不含 0 的情况,若乘积能分出 k 个 10 末尾就有 k 个 0
- 所以直接判断有几个数是 10 的倍数即可——吗?
- 考虑一条路径,形如:
- 我们发现,乘积是 9000,末尾有三个 0,但是该序列不存在 10 的倍数
- 可以发现,10=2×5
- 因此,我们不能去记录 10 的倍数,而是记录每个数对于 2 和 5 的贡献
- 在代码实现上,可以考虑将状态设置成 dp[i][j][0/1]
- 第一问做完了,进入第二问——打印路径
- 我们在 dp 过程中进行判断,贪心没有后效性
- 比较当前状态与上一步的状态,若对 2 和 5 贡献少的就记录方向
- 最后打印路径
细节处理:
- 0 的特判(如果矩阵中存在 0,则需要特殊标记)
- 2 和 5 要分开处理,两者互不干扰
代码:
代码缩进可能出现一部分神秘问题,不喜勿喷哈!
点击查看代码
#include<iostream>
#include<vector>
using namespace std;
const int maxn=1024;
int n;
int dp[maxn][maxn][2];
char dir[maxn][maxn][2];
vector<char> ans;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
bool flag=false;
int zx,zy;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int x;
cin>>x;
if(!x){
flag=true;
zx=i;
zy=j;
continue;
}
while(x%2==0){
dp[i][j][0]++;
x/=2;
}
while(x%5==0){
dp[i][j][1]++;
x/=5;
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==1&&j==1){
continue;
}else if(i>1&&j>1){
if(dp[i-1][j][0]>dp[i][j-1][0]){
dir[i][j][0]='R';
dp[i][j][0]+=dp[i][j-1][0];
}else{
dir[i][j][0]='D';
dp[i][j][0]+=dp[i-1][j][0];
}
if(dp[i-1][j][1]>dp[i][j-1][1]){
dir[i][j][1]='R';
dp[i][j][1]+=dp[i][j-1][1];
}else{
dir[i][j][1]='D';
dp[i][j][1]+=dp[i-1][j][1];
}
}else if(i>1&&j==1){
dir[i][j][0]=dir[i][j][1]='D';
dp[i][j][0]+=dp[i-1][j][0];
dp[i][j][1]+=dp[i-1][j][1];
}else if(j>1&&i==1){
dir[i][j][0]=dir[i][j][1]='R';
dp[i][j][0]+=dp[i][j-1][0];
dp[i][j][1]+=dp[i][j-1][1];
}
}
}
int d=(dp[n][n][1]<dp[n][n][0]?1:0);
if(flag&&dp[n][n][d]>0){
cout<<1<<endl;
for(int i=1;i<zx;i++){
cout<<'D';
}
for(int i=1;i<zy;i++){
cout<<'R';
}
for(int i=zx+1;i<=n;i++){
cout<<'D';
}
for(int i=zy+1;i<=n;i++){
cout<<'R';
}
return 0;
}
cout<<dp[n][n][d]<<endl;
int x=n,y=n;
vector<char> ans;
while(x!=1||y!=1){
ans.push_back(dir[x][y][d]);
if(dir[x][y][d]=='D'){
x--;
}else{
y--;
}
}
for(int i=ans.size()-1;i>=0;i--){
cout<<ans[i];
}
return 0;
}
----------------------不 ~ 开 ~ 心 ~ 的分割线----------------------
CF2C
题面(可从下方链接跳转看原题题面):
给定三个圆,找到一个点,使得到三个圆的两切线张角相同,取最大张角的那个点输出,没有则不输出
序言 & 结论:
我讨厌计算几何(或者叫解析几何?)
需要很多的数学知识(甚至本题我没有用到任何的 OI 知识)
所以直接略过看代码吧!
推理过程:
细节处理:
- 不要开 long double,直接寄
- 建议输入输出选用 scanf,printf
- 公式的计算可以现在草纸上书写一遍,再敲代码(这是一个悲伤的故事~)
代码:
其实这种题目直接拿代码走人吧!
本蒟蒻自己写完的代码自己都看不懂了(得亏有部分注释)
点击查看代码
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const double eps=1e-6;
//点
struct node{
double x,y;
};
//向量
struct vect{
double x,y;
};
//圆
struct circ{
double x,y,r;
};
inline double node_dis(node X,node Y){
double dx=X.x-Y.x,dy=X.y-Y.y;
return sqrt(dx*dx+dy*dy);
}
inline double vect_len(vect X){
return sqrt(X.x*X.x+X.y*X.y);
}
int main(){
double r1,r2,r3;
node A,B,C,M,N;
vect u,v;
scanf("%lf%lf%lf",&A.x,&A.y,&r1);
scanf("%lf%lf%lf",&B.x,&B.y,&r2);
scanf("%lf%lf%lf",&C.x,&C.y,&r3);
//向量初始化
u=(vect){B.x-A.x,B.y-A.y};
v=(vect){C.x-A.x,C.y-A.y};
//直线与直线
if(r1==r2&&r1==r3){
//两直线平行
if(u.x*v.y==v.x*u.y){
return 0;
}
//垂直
vect du=(vect){-u.y,u.x};
vect dv=(vect){-v.y,v.x};
M=(node){A.x+u.x*0.5,A.y+u.y*0.5};
N=(node){A.x+v.x*0.5,A.y+v.y*0.5};
double t1;
//解方程
t1=(N.x*dv.y-N.y*dv.x+dv.x*M.y-dv.y*M.x)/(du.x*dv.y-du.y*dv.x);
printf("%.5f %.5f\n",M.x+du.x*t1,M.y+du.y*t1);
}
//直线与圆
if(r1==r2&&r1!=r3){
vect _u=(vect){-u.y,u.x};
M=(node){A.x+u.x*0.5,A.y+u.y*0.5};
//求圆与对称轴的交点
node P=(node){A.x+v.x*(r1/(r1+r3)),A.y+v.y*(r1/(r1+r3))};
node Q=(node){C.x+v.x*(r3/(r1-r3)),C.y+v.y*(r3/(r1-r3))};
//阿氏圆
circ N=(circ){(P.x+Q.x)*0.5,(P.y+Q.y)*0.5,node_dis(P,Q)*0.5};
//向量计算
vect MN=(vect){N.x-M.x,N.y-M.y};
double dMN=vect_len(MN),d_u=vect_len(_u);
double ddu=(_u.x*MN.x+_u.y*MN.y)/d_u;
if(ddu*ddu+N.r*N.r<dMN*dMN){
return 0;
}
double d=sqrt(dMN*dMN-ddu*ddu);
double dd1=ddu-sqrt(N.r*N.r-d*d);
double dd2=ddu+sqrt(N.r*N.r-d*d);
//反手一个解得
node T1=(node){M.x+_u.x*dd1/d_u,M.y+_u.y*dd1/d_u};
node T2=(node){M.x+_u.x*dd2/d_u,M.y+_u.y*dd2/d_u};
if(node_dis(T1,A)<node_dis(T2,A)){
printf("%.5f %.5f\n",T1.x,T1.y);
}else{
printf("%.5f %.5f\n",T2.x,T2.y);
}
}
//圆与直线
if(r1!=r2&&r1==r3){
vect _v=(vect){-v.y,v.x};
N=(node){A.x+v.x*0.5,A.y+v.y*0.5};
node P=(node){A.x+u.x*(r1/(r1+r2)),A.y+u.y*(r1/(r1+r2))};
node Q=(node){B.x+u.x*(r2/(r1-r2)),B.y+u.y*(r2/(r1-r2))};
circ M=(circ){(P.x+Q.x)*0.5,(P.y+Q.y)*0.5,node_dis(P,Q)*0.5};
vect NM=(vect){M.x-N.x,M.y-N.y};
double dNM=vect_len(NM),d_v=vect_len(_v);
double ddv=(_v.x*NM.x+_v.y*NM.y)/d_v;
if(ddv*ddv+M.r*M.r<dNM*dNM){
return 0;
}
double d=sqrt(dNM*dNM-ddv*ddv);
double dd1=ddv-sqrt(M.r*M.r-d*d);
double dd2=ddv+sqrt(M.r*M.r-d*d);
node T1=(node){N.x+_v.x*dd1/d_v,N.y+_v.y*dd1/d_v};
node T2=(node){N.x+_v.x*dd2/d_v,N.y+_v.y*dd2/d_v};
if(node_dis(T1,A)<node_dis(T2,A)){
printf("%.5f %.5f\n",T1.x,T1.y);
}else{
printf("%.5f %.5f\n",T2.x,T2.y);
}
}
//圆与圆
if(r1!=r2&&r1!=r3){
node P,Q;
P=(node){A.x+u.x*(r1/(r1+r2)),A.y+u.y*(r1/(r1+r2))};
Q=(node){B.x+u.x*(r2/(r1-r2)),B.y+u.y*(r2/(r1-r2))};
circ M=(circ){(P.x+Q.x)*0.5,(P.y+Q.y)*0.5,node_dis(P,Q)*0.5};
P=(node){A.x+v.x*(r1/(r1+r3)),A.y+v.y*(r1/(r1+r3))};
Q=(node){C.x+v.x*(r3/(r1-r3)),C.y+v.y*(r3/(r1-r3))};
circ N=(circ){(P.x+Q.x)*0.5,(P.y+Q.y)*0.5,node_dis(P, Q)*0.5 };
double d=node_dis((node){M.x,M.y},(node){N.x,N.y});
//两圆check交点
if(d>=M.r+N.r){
return 0;
}
if(d+M.r<=N.r){
return 0;
}
if(d+N.r<=M.r){
return 0;
}
if(abs(M.x-N.x)<eps&&abs(M.y-N.y)<eps){
return 0;
}
//余弦定理求角
double alpha=acos((d*d+M.r*M.r-N.r*N.r)/(d*M.r*2.0));
double cosr=M.r*cos(alpha);
double sinr=M.r*sin(alpha);
//向量,还是向量!
vect MN=(vect){N.x-M.x,N.y-M.y};
node S=(node){M.x+MN.x*cosr/vect_len(MN),M.y+MN.y*cosr/vect_len(MN)};
vect _MN=(vect){-MN.y,MN.x};
node T1=(node){S.x+_MN.x*sinr/vect_len(_MN),S.y+_MN.y*sinr/vect_len(_MN)};
node T2=(node){S.x-_MN.x*sinr/vect_len(_MN),S.y-_MN.y*sinr/vect_len(_MN)};
if(node_dis(T1,A)<node_dis(T2,A)){
printf("%.5f %.5f\n",T1.x,T1.y);
}else{
printf("%.5f %.5f\n",T2.x,T2.y);
}
}
return 0;
}
完结撒花!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具