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)\) 能获得的最大积分。答案为:

\[\max\{ f_{lv,x,y}+(k-lv) \times a_{i,j} \} \]

每次从一层向更高层更新最长路,注意如果不初始化为极小值(比如 \(-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;
}
posted @ 2024-11-08 21:44  Jerrycyx  阅读(7)  评论(0编辑  收藏  举报