前言
这场比赛我发挥欠佳,各种小失误不断,也需要我自己去反思吧,以后打模拟赛也要有选择性地去打,毕竟一天 \(3\) 场比赛肯定会有影响。
正文
1. 子数整数
错点
考试的时候蒟蒻把 No
打成了 NO
。
分析
这道题实际上就是个小模拟,您只要会 \(for\) 循环就可以轻松过掉此题。
答案是直接统计一下就好了。
我们可以直接从 10000
到 30000
循环,算出 s1
、 s2
、 s3
。
for(int i=10000;i<=30000;i++){ int g=i%10,s=i/10%10,b=i/100%10,q=i/1000%10,w=i/10000%10,s1=100*w+10*q+b,s2=100*q+10*b+s,s3=100*b+s*10+g;//s1,s2,s3的计算 if(s1%k==0&&s2%k==0&&s3%k==0)writen(i),f=1; }
总代码
#include <bits/stdc++.h> using namespace std; template<typename T>inline void read(T &FF){ T RR=1;FF=0;char CH=getchar(); for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1; for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48); FF*=RR; } template<typename T>void write(T x){ if(x<0)putchar('-'),x*=-1; if(x>9)write(x/10); putchar(x%10+48); } template<typename T>void writen(T x){ write(x); puts(""); } int k,s1,s2,s3,g,s,b,q,w; bool f; int main(){ read(k); for(int i=10000;i<=30000;i++){ int g=i%10,s=i/10%10,b=i/100%10,q=i/1000%10,w=i/10000%10,s1=100*w+10*q+b,s2=100*q+10*b+s,s3=100*b+s*10+g;//s1,s2,s3的计算 if(s1%k==0&&s2%k==0&&s3%k==0)writen(i),f=1; }if(f==0)puts("No");//考试时这里直接挂掉,打成了NO return 0; }
2. 安全逃离
错点
蒟蒻向老师报告题目有问题后没有认真看题,结果就死了。
分析
做到这道题的时候,我们考到数据范围很小,考虑高复杂度的做法。
我的做法是最暴力的做法,复杂度大概是 \(O(cr^2 \times + rc^2 \times n)\),面对这么小的这么小的数据,这个做法显然能过。
我们先写一个 \(check\) 函数,判断此时的矩阵是否安全。
bool check(){ for(int i=1;i<=r;++) for(int j=1;j<=c;j++) if(a[i][j]){//此点有牛 int s=0; for(int l=1;l<=i;l++) if(a[l][j]){ s++; break; } for(int l=j;l<=n;l++) if(a[i][l]){ s++; break; } if(s==2)return false;//如果有牛上面和右面都被堵死了,这个矩阵显然是不安全的 } return true; }
当然还有更快速地写法啦
bool save(){ for(int i=1;i<=k;i++){ int flag=0; for(int l=x[i]-1;l>=1;l--) if(a[l][y[i]]){ flag++; break; } for(int l=y[i]+1;l<=n;l++) if(a[x[i]][l]){ flag++; break; } if(flag==2)return false; } return true; }
我们再写一个函数去枚举移掉哪头牛
void work(){ int f=1; for(int i=1;i<=k;i++){ a[x[i]][y[i]]--; if(save()){//如果安全 f=0; write(i);//输出 puts(""); } a[x[i]][y[i]]++; }if(f)puts("-1");//无解 }
总代码
#include <bits/stdc++.h> using namespace std; template<typename T>inline void read(T &FF){ T RR=1;FF=0;char CH=getchar(); for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1; for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48); FF*=RR; } template<typename T>void write(T x){ if(x<0)putchar('-'),x*=-1; if(x>9)write(x/10); putchar(x%10+48); } const int MAXN=50+10,MAXK=100+10; int a[MAXN][MAXN],n,m,k,x[MAXK],y[MAXK]; bool save(){ for(int i=1;i<=k;i++){ int flag=0; for(int l=x[i]-1;l>=1;l--) if(a[l][y[i]]){ flag++; break; } for(int l=y[i]+1;l<=n;l++) if(a[x[i]][l]){ flag++; break; } if(flag==2)return false; } return true; } void work(){ int f=1; for(int i=1;i<=k;i++){ a[x[i]][y[i]]--;//移除 if(save()){ f=0; write(i); puts(""); } a[x[i]][y[i]]++;//注意回溯 }if(f)puts("-1"); } int main(){ read(n);read(m);read(k); for(int i=1;i<=k;i++){ read(x[i]);read(y[i]); a[x[i]][y[i]]++; } if(save())puts("0"); else work(); return 0; }
3. 阶乘问题
错点
蒟蒻竟然把 \(/ 10\) 打成了 \(\% 10\)。
分析
做法显然
由于数字极大,我们应该先对 \(1\)~\(n\) 的这些数字把末尾的 \(0\) 全部删掉
while(x%10==0)x/=10;
再把乘好后的 \(s\) 末尾的 \(0\) 也都去掉。
关于为什么末尾会有 \(0\),我来举个例子 \(2 \times 5=10\) ,所以我们还要再筛一遍 \(0\)。
while(s%10==0)s/=10;
总代码
#include <bits/stdc++.h> using namespace std; template<typename T>inline void read(T &FF){ T RR=1;FF=0;char CH=getchar(); for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1; for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48); FF*=RR; } template<typename T>void write(T x){ if(x<0)putchar('-'),x*=-1; if(x>9)write(x/10); putchar(x%10+48); } int main(){ int n,s=1; read(n); for(int i=1;i<=n;i++){ int x=i; while(x%10==0)x/=10; s*=x; while(s%10==0)s/=10; }write(s); return 0; }
4. 拱猪计分
分析
典型的大模拟。
明白了什么叫 \(1h\) 读题,\(0.2h\) 写代码
我们从读入开始
读入
for(int i=1;i<=4;i++){ read(size[i]); for(int j=1;j<=size[i];j++){ char k;int p; cin>>k>>p; if(k=='H')card[i][p]=true; else if(k=='S')card[i][14]=true; else if(k=='D')card[i][15]=true; else if(k=='C')card[i][16]=true; } }
我们用 \(card_{i,j}\) 记录第 \(i\) 个人,有没第 \(j\) 张得分牌。
计分
for(int i=1;i<=4;i++){ bool flag=true; for(int j=1;j<=13;j++) if(card[i][j]==false){ flag=false; break; } if(flag){//有没有红心牌 int ans=200;//所有红心牌以+200分计算。 if(card[i][14]&&card[i][15])ans=500;//若S12、D11皆在吃下所有红心牌之一家,则此玩家得+500分。 else ans=ans+card[i][14]*fraction[14]+card[i][15]*fraction[15]; if(card[i][16])ans*=2;//若除了C10还有其它计分牌,则将其它计分牌所得分数加倍计算。 work(ans);//输出 putchar(32);//输出 }else{ bool flg=true; for(int j=1;j<=15;j++) if(card[i][j]==true){ flg=false; break; } if(flg){ if(card[i][16])work(50);//若持有C10的玩家只有该张牌而没有任何其它牌则得+50分 else work(0);//若未持有这16张牌之任一张则以得零分计算。 putchar(32);//输出 }else{ int ans=0; for(int j=1;j<=15;j++) if(card[i][j])ans+=fraction[j]; if(card[i][16])ans*=2; work(ans);//输出 putchar(32);//输出 } } }puts("");
总代码
#include <bits/stdc++.h> using namespace std; template<typename T>inline void read(T &FF){ T RR=1;FF=0;char CH=getchar(); for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1; for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48); FF*=RR; } template<typename T>void write(T x){ if(x<0)putchar('-'),x*=-1; if(x>9)write(x/10); putchar(x%10+48); } void work(int x){ if(x<0)cout<<x; else if(x>0)cout<<'+'<<x; else cout<<x; } const int fraction[17]={0,-50,-2,-3,-4,-5,-6,-7,-8,-9,-10,-20,-30,-40,-100,+100,0}; int size[5]; bool card[5][17]; int main(){ while(1){ memset(card,false,sizeof(card)); for(int i=1;i<=4;i++){ read(size[i]); for(int j=1;j<=size[i];j++){ char k;int p; cin>>k>>p; if(k=='H')card[i][p]=true; else if(k=='S')card[i][14]=true; else if(k=='D')card[i][15]=true; else if(k=='C')card[i][16]=true; } } if(size[1]+size[2]+size[3]+size[4]==0)return 0; for(int i=1;i<=4;i++){ bool flag=true; for(int j=1;j<=13;j++) if(card[i][j]==false){ flag=false; break; } if(flag){ int ans=200; if(card[i][14]&&card[i][15])ans=500; else ans=ans+card[i][14]*fraction[14]+card[i][15]*fraction[15]; if(card[i][16])ans*=2; work(ans); putchar(32); }else{ bool flg=true; for(int j=1;j<=15;j++) if(card[i][j]==true){ flg=false; break; } if(flg){ if(card[i][16])work(50); else work(0); putchar(32); }else{ int ans=0; for(int j=1;j<=15;j++) if(card[i][j])ans+=fraction[j]; if(card[i][16])ans*=2; work(ans); putchar(32); } } }puts(""); } return 0; }
5. 变色龙
错点
\(bfs\) 的 \(priority\)_\(queue\) 优化写假了。
分析
最短路问题。
为什么能使用 \(dij\) 跑最短路?明显会 \(T\) 啊!
点数:\(2000 \times 2000=400,0000\)
边数:小学奥数之等差数列
\(\begin{matrix}\underbrace{0+4+\cdots+7996}\\2000项\end{matrix}=(7996+0) \times 2000 \div 2=7996 \times 1000=799,6000\)
我们知道 \(dij\) 的复杂度是 \(O(n \log m)\)
简单计算一下
\(\log m=\log 7996000 \approx 26\),\(26 \times 4000000=1,0400,0000 > 1,0000,0000\)
所以显然是会 \(T\) 掉的(除非玄学卡常,比如:等式展开之类的)
而 \(bfs\) 的期望复杂度是 \(O(n+m)\) 的。
\(bfs\) 大概就是这种写法
q.push((node){xx,yy,0}); while(q.size()){ node f=q.top(); q.pop(); for(int i=0;i<4;i++){ int x=f.x+dx[i],y=f.y+dy[i]; if(x>=1&&x<=n&&y>=1&&y<=m){ int as=ans[f.x][f.y]; if(a[x][y]!=a[f.x][f.y])as++; if(as<ans[x][y])ans[x][y]=as,q.push((node){x,y}); } } }
总代码
#include <bits/stdc++.h> using namespace std; template<typename T>inline void read(T &FF){ T RR=1;FF=0;char CH=getchar(); for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1; for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48); FF*=RR; } template<typename T>void write(T x){ if(x<0)putchar('-'),x*=-1; if(x>9)write(x/10); putchar(x%10+48); } struct node{ int x,y; }; const int MAXN=2000+10; int n,m,xx,yy,a[MAXN][MAXN],ans[MAXN][MAXN]; int dx[4]={-1,1,0,0}; int dy[4]={0,0,-1,1}; queue<node>q; int main(){ read(n);read(m);read(xx);read(yy); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) read(a[i][j]); q.push((node){xx,yy,0}); while(q.size()){ node f=q.top(); q.pop(); for(int i=0;i<4;i++){ int x=f.x+dx[i],y=f.y+dy[i]; if(x>=1&&x<=n&&y>=1&&y<=m){ int as=ans[f.x][f.y]; if(a[x][y]!=a[f.x][f.y])as++; if(as<ans[x][y])ans[x][y]=as,q.push((node){x,y}); } } } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++) cout<<ans[i][j]<<" "; cout<<endl; } return 0; }
6. 和谐分组
讲解二分
终于是正常一点的题目了,并且是我最喜欢的算法(蒟蒻最喜欢的算法是并查集、二分、dp、LCA)
二分是确定一个答案然后对其分析,而答案常常有这样一种情况:
左闭右开
右闭左开
题目通常会让我们找符合条件的最大值或最小值。
以这道题为例,就是去在可能的谐度度中找一个最小的。
比答案大的都可以,不答案小的都不可以。
这个我们叫右闭左开。
而比答案小的都可以,不答案大的都不可以。
这个我们叫左闭右开。
二分顾名思义,就是二分。
左闭右开:
int l=0,r=INT_MAX/2; while(l+1<r){ int mid=(l+r)>>1; if(check(mid))l=mid;//这里不同 else r=mid;//这里不同 }
右闭左开:
int l=0,r=INT_MAX/2; while(l+1<r){ int mid=(l+r)>>1; if(check(mid))r=mid;//这里不同 else l=mid;//这里不同 }
分析
现在我们已经会了二分,我们可以愉快地做这道题了。
我们可以开始写 \(check\) 函数:
bool check(int x){//check函数顾名思义,就是用来反对我们的答案x是否可行 int maxn=a[1],minn=a[1],s=1;//初始化 for(int i=2;i<=n;i++){ maxn=max(maxn,a[i]);//最大 minn=min(minn,a[i]);//最小 if(maxn-minn>x){ s++;//新分一个组 maxn=a[i];//初始化 minn=a[i];//初始化 } } return s<=k;//如果组数小于等于k,就说明不可行 }
总代码
#include <bits/stdc++.h> using namespace std; template<typename T>inline void read(T &FF){ T RR=1;FF=0;char CH=getchar(); for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1; for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48); FF*=RR; } template<typename T>void write(T x){ if(x<0)putchar('-'),x*=-1; if(x>9)write(x/10); putchar(x%10+48); } const int MAXN=1e5+10; int n,k,a[MAXN]; bool check(int x){ int maxn=a[1],minn=a[1],s=1; for(int i=2;i<=n;i++){ maxn=max(maxn,a[i]); minn=min(minn,a[i]); if(maxn-minn>x){ s++; maxn=a[i]; minn=a[i]; } } return s<=k; } int main(){ read(n);read(k); for(int i=1;i<=n;i++)read(a[i]); int l=0,r=INT_MAX/2; while(l+1<r){ int mid=(l+r)>>1; if(check(mid))r=mid; else l=mid; }write(r); return 0; }
来源:https://www.cnblogs.com/zhaohaikun/p/12427657.html