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;
}