AtCoder Beginner Contest 358 - VP记录
Preface
这次的动规题真的多,起码有三道都是。
赛时做完 ABCD 以后就去攻 G 去了,可惜犯了煞笔错误搞 WA 了。
赛后补 F 的时候思路代码啥的都挺顺的(没看题解独立切的蓝题),就是犯了更煞笔的错误,成消愁🤡了(详见下方 F 题处),不然可以一个小时切了的。
总而言之,读题(特别是输入输出格式)一定要仔细,查半天查不出 Hack 数据的时候检查一下题意是否理解正确,输入输出是否符合题意。
A - Welcome to AtCoder Land
本场比赛主角:AtCoder Land。
点击查看代码
#include<cstdio> #include<cstring> using namespace std; const int N=15; char s[N],t[N]; int main() { scanf("%s%s",s,t); if(!strcmp(s,"AtCoder")&&!strcmp(t,"Land")) printf("Yes\n"); else printf("No\n"); return 0; }
B - Ticket Counter
点击查看代码
#include<cstdio> #include<algorithm> using namespace std; const int N=105; int n,t,a[N]; int main() { scanf("%d%d",&n,&t); int ti=0; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); ti=max(ti,a[i])+t; printf("%d\n",ti); } return 0; }
C - Popcorn
Pop
+Corn
=****
——xyz
据说我是最优解(时间全是 \(0\) ms),其实就是状态压缩位运算加了点速而已,本质还是暴力。
点击查看代码
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=15; int n,m; char str[N]; int bt[N],now; int main() { scanf("%d%d",&n,&m); for(int i=0;i<n;i++) { scanf("%s",str); for(int j=0;j<m;j++) if(str[j]=='o') bt[i]|=1<<j; } int ans=0x3f3f3f3f; for(int z=0;z<1<<n;z++) { int now=0,cnt=0; for(int i=0;i<n;i++) if(z>>i&1) now|=bt[i],cnt++; if(now==(1<<m)-1) ans=min(ans,cnt); } printf("%d\n",ans); return 0; }
D - Souvenirs
贪心,每次找距离 \(b_i\) 最近且大于等于它的 \(a\),可以用二分(可以 lower_bound
)。
\(a\) 需要排个序,别问我为什么,问就是试出来的。
点击查看代码
#include<cstdio> #include<algorithm> using namespace std; const int N=2e5+5,M=2e5+5; int n,m,a[N],b[M]; long long ans; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=m;i++) scanf("%d",&b[i]); sort(a+1,a+n+1); sort(b+1,b+m+1); bool have_sol=true; for(int i=1;i<=m;i++) { int p=lower_bound(a+1,a+n+1,b[i])-a; if(p==n+1) { have_sol=false; break; } ans+=a[p],a[p]=0; } if(have_sol) printf("%lld\n",ans); else printf("-1\n"); return 0; }
E - Alphabet Tiles
差评,难度不够均匀,这次都没有黄题,直接橙跳绿了。
动态规划,两下就推出了所有的状态表示和转移方程,可惜代码里循环范围搞错了(具体位置见代码注释,这么写的原因也在注释里)。
赛后看了题解才知道错哪里,思路和这篇题解相似。
#include<cstdio> #include<algorithm> using namespace std; const int N=1005,M=30,P=998244353; int n,c[M]; int f[M][N],C[N][N]; int main() { scanf("%d",&n); C[0][0]=1; for(int i=1;i<=n;i++) for(int j=0;j<=i;j++) C[i][j]=j?(C[i-1][j]+C[i-1][j-1])%P:1; for(int i=1;i<=26;i++) scanf("%d",&c[i]); f[0][0]=1; for(int i=1;i<=26;i++) { for(int j=0;j<=n;j++) { /* * j需要从0开始,这样可以使f[i][0]均等于1 * 然后在计算的时候,就可以计算到单字符组成的串及其衍生 * 否则,只有f[0][0]=1,这样最初只有串"A"被计入方案 * 后续所有字符串都是在单字符'A'上进行修改得到的,方案就不全面 */ for(int k=0;k<=min(j,c[i]);k++) f[i][j]=(f[i][j]+1ll*C[j][k]*f[i-1][j-k])%P; printf("%d ",f[i][j]); } putchar('\n'); } int ans=0; for(int i=1;i<=n;i++) ans=(ans+f[26][i])%P; printf("%d\n",ans); return 0; }
F - Easiest Maze
全体目光,向我看齐,我宣布个事,我是个傻逼!!!
发疯完毕,有解时没有输出 Yes
血调一个半小时,最后发现我是 Joker🤡。
最后补的一道题,基础思路为先构造出一条从起点到终点的直线路径,然后再逐步拉伸变长。
构造部分我是先生成的行走方向序列,再根据这个方向序列生成迷宫。
回头有空在洛谷上发一篇题解。
#include<cstdio> using namespace std; const int N=105,M=105; int n,m,k; char dir[N][M]; void Build_dir() { for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) dir[i][j]='-'; for(int i=1;i<=n;i++) dir[i][m]='D'; int rest=k-n; for(int i=1;i+1<=n;i+=2) { if(!rest) break; for(int j=m-1;j>=1;j--) { if(!rest) break; dir[i][j+1]='L'; dir[i][j]='D'; dir[i+1][j]='R'; rest-=2; } } for(int i=1;i<=m;i+=2) { if(!rest) break; dir[n-1][i]='D'; dir[n][i]='R'; dir[n][i+1]='U'; rest-=2; } return; } char ans[N<<1][M<<1]; void Solve() { Build_dir(); for(int i=1;i<=(n<<1|1);i++) ans[i][1]=ans[i][m<<1|1]='+'; for(int i=1;i<=(m<<1|1);i++) ans[1][i]=ans[n<<1|1][i]='+'; ans[1][m<<1]='S',ans[n<<1|1][m<<1]='G'; for(int i=1;i<=(n<<1|1);i+=2) for(int j=1;j<=(m<<1|1);j+=2) ans[i][j]='+'; for(int i=2;i<(n<<1|1);i+=2) for(int j=2;j<(m<<1|1);j+=2) ans[i][j]='o'; for(int i=3;i<(n<<1|1);i+=2) for(int j=2;j<(m<<1|1);j+=2) { int x=(i-1)>>1,y=j>>1; if(dir[x][y]=='D'||dir[x+1][y]=='U') ans[i][j]='.'; else ans[i][j]='-'; } for(int i=2;i<(n<<1|1);i+=2) for(int j=3;j<(m<<1|1);j+=2) { int x=i>>1,y=(j-1)>>1; if(dir[x][y]=='R'||dir[x][y+1]=='L') ans[i][j]='.'; else ans[i][j]='|'; } printf("Yes\n"); for(int i=1;i<=(n<<1|1);i++) { for(int j=1;j<=(m<<1|1);j++) putchar(ans[i][j]); putchar('\n'); } return; } bool check() { if((n&1)!=(k&1)) return false; int maxpath=n*m; if((n&1)&&!(m&1)) maxpath--; if(k>maxpath||k<n) return false; return true; } int main() { scanf("%d%d%d",&n,&m,&k); if(check()) Solve(); else printf("No\n"); return 0; }
G - AtCoder Tour
赛时主攻的题,可惜最长路没有初始化为 -INF
而是 0
导致挂了。
最优策略一定是一路走到某个位置,然后一直待到结束,证明可以参考洛谷题解。
建立分层图,\(f_{lv,x,y}\) 表示第 \(lv\) 步走到坐标 \((x,y)\) 能获得的最大积分。答案为:
每次从一层向更高层更新最长路,注意如果不初始化为极小值(比如 \(-10^{18}\) 之类的),可能会导致求得的答案不是以题目所要求为起点。
同时也不可以按照常规 DP 找四个方向的最大积分,因为最长路可能会绕着弯走(但是不会形成环)。
#include<cstdio> #include<queue> #include<algorithm> using namespace std; const int N=55,M=55,K=2505; int n,m,k,sx,sy; long long a[N][M]; long long f[K][N][M],ans; const int dx[5]={0,1,-1,0,0}; const int dy[5]={0,0,0,1,-1}; int main() { scanf("%d%d%d",&n,&m,&k); scanf("%d%d",&sx,&sy); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { scanf("%lld",&a[i][j]); for(int lv=0;lv<=min(n*m,k);lv++) f[lv][i][j]=-1e18; //IMPORTANT!!! } for(int l=0;l<5;l++) { int tx=sx+dx[l],ty=sy+dy[l]; f[1][tx][ty]=a[tx][ty]; } ans=1ll*k*a[sx][sy]; for(int lv=1;lv<=min(n*m,k);lv++) for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { if(f[lv][i][j]==-1e18) continue; ans=max(ans,f[lv][i][j]+1ll*(k-lv)*a[i][j]); for(int l=0;l<5;l++) { int tx=i+dx[l],ty=j+dy[l]; f[lv+1][tx][ty]=max(f[lv+1][tx][ty],f[lv][i][j]+a[tx][ty]); } } printf("%lld\n",ans); return 0; }
本文采用 「CC-BY-NC 4.0」 创作共享协议,转载请注明作者及出处,禁止商业使用。
作者:Jerrycyx,原文链接:https://www.cnblogs.com/jerrycyx/p/18535939
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步