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