Monoxer Programming Contest 2024(AtCoder Beginner Contest 345)题解

A - Leftrightarrow(A - Leftrightarrow

#include<bits/stdc++.h>
using namespace std;
int main()
{
	string s;
	cin>>s;
	int l=s.size()-1;
	if(s[0]=='<'&&s[l]=='>')
	{
		int flag=1;
		for(int i=1;i<l;i++)
		{
			if(s[i]!='=') 
			{
				flag=0;
				break;
			}
		}
		if(flag) printf("Yes\n");
		else printf("No\n");
	}
	else printf("No\n");
}

B - Integer Division Returns(B - Integer Division Returns

直接通过最后一位是否为0来判断,注意正负不同

#include<bits/stdc++.h>
using namespace std;
int main()
{
	long long x;
	scanf("%lld",&x);
	if(x>=0)
	{
		if(x%10) cout<<x/10+1;
		else cout<<x/10;
	}
	else 
	{
		cout<<x/10;
	}
}

C - One Time Swap(C - One Time Swap

可以通过遍历来找每个字符前面有多少个和它不同的字符,两者一旦交换就产生新字符串,然后顺便统计一下,某个字符前面是否有与之相同的字符,如果有,最后的结果+1,表示两者交换,字符串不变的情况。

#include<bits/stdc++.h>
using namespace std;
int st[30];
int main()
{
	string s;
	cin>>s;
	long long ans=0,flag=0;
	for(int i=0;i<s.size();i++)
	{
		int d=s[i]-'a';
		for(int j=0;j<26;j++)
		{
			if(j!=d) ans+=st[j];
			else 
			{
				if(st[j]) flag=1;
			}
		}
		st[d]++;
	}
	printf("%lld",ans+flag);
}

D - Tiling(D - Tiling

从n块瓷砖中选择一部分,使得被选瓷砖完全覆盖h*w的矩形,问能否实现。

思路这题的数据比较小,可以暴力。即我们从左往右一排一排的找,找到矩阵中第一个没放瓷砖的地方,枚举没用过的瓷砖,看看是否有左上角可以放在这个位置的瓷砖,如此搜索就可实现。

#include<bits/stdc++.h>
using namespace std;
int sta[20][20];
int st[30];
int a[30],b[30];
int n,h,w;
int ans;
void dfs(int x,int y)
{
	while(sta[x][y])
	{
		y++;
		if(y>w)
		{
			x++;
			y=1;
		}
		if(x>h) break;
	}
	if(x>h) //x大于h,那么就说明前h行都填满了
	{
		ans = 1;
		return ;
	}
	for(int i=1;i<=n;i++)
	{
		if(st[i]) continue;
		int flag=1;
		for(int ix=0;ix<a[i];ix++)
			for(int iy=0;iy<b[i];iy++)//左上的格子直接填
				if(x+ix<=h&&y+iy<=w)
				{
					if(!sta[x+ix][y+iy]) sta[x+ix][y+iy] = i;
					else 
					{
						flag=0;
						break;
					}
				}
				else 
				{
					flag=0;
					break;
				}
			
		if(flag) 
		{
			st[i]=1;
			dfs(x,y);
			st[i]=0;
		}
		
		for(int ix=0;ix<a[i];ix++)
			for(int iy=0;iy<b[i];iy++)
				if(x+ix<=h&&y+iy<=w)
					if(sta[x+ix][y+iy]==i) sta[x+ix][y+iy] = 0;
					
		if(a[i]!=b[i])
		{
			flag=1;
			for(int ix=0;ix<a[i];ix++)
				for(int iy=0;iy<b[i];iy++)
					if(x+iy<=h&&y+ix<=w)
					{
						if(!sta[x+iy][y+ix]) sta[x+iy][y+ix] = i;
						else 
						{
							flag=0;
							break;
						}
					}
					else 
					{
						flag=0;
						break;
					}
			if(flag) 
			{
				st[i]=1;
				dfs(x,y);
				st[i]=0;
			}
			for(int ix=0;ix<a[i];ix++)
				for(int iy=0;iy<b[i];iy++)
					if(x+iy<=h&&y+ix<=w)
						if(sta[x+iy][y+ix]==i) sta[x+iy][y+ix] = 0;
		}
	}
	return;
}
int main()
{
	scanf("%d%d%d",&n,&h,&w);
	for(int i=1;i<=h;i++)
		for(int j=1;j<=w;j++) 
			sta[i][j]=0;
	for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]);
	dfs(1,1);
	if(ans) printf("Yes\n");
	else printf("No\n");
}

E - Colorful Subsequence(E - Colorful Subsequence

有一排彩色气球,共有n,每个气球有一个颜色和价值,我们需要从中删除k个气球,使得相邻两个气球颜色不同,且剩下气球的价值最大。

思路:我最开始想的是将重复部分只留一个,统计要删多少个,如果删除的数目小于k的话,再将剩下的里面小的删除。但是对于aabaa这种情况,如果要删3个,且b最小,那么就会出现aa的情况,显然不可以。

这题用的是dp。

先看暴力层面定义:dp[i][j][k]表示前i个中,删除j个,结尾颜色是k的最大价值。

对于第i个,我们有留与不留两种选择

如果留,dp[i][j][k]=max(dp[i-1][j][k'])+vi//从前i-1中选,删除j个,结尾为k'(k'不等于k)

如果不留,dp[i][j][k]=max(dp[i-1][j-1][k'])如果不留,就可以从各种颜色中找最值

注意到这里有一种特殊情况,j=0,对于这种情况,i必须留,因为没有操作可以用来删它,那么一旦遍历到不能留的位置,赋成负无穷即可。

上面的方法虽好,但是显然时间复杂度和空间复杂度都太高了,我们观察可以发现实际上用到的就是(i-1,j)和(i-1,j-1)在不同颜色下的最值,那么我们是否可以将这个最值维护出来?显然是可以的,不过要注意,我们需要考虑结尾的颜色,所以最大值不一定能用,故而应该同时维护一个次大值,一定要保证最大值和次大值的颜色不同,将这两个值和两种颜色记一下,然后不断遍历,不断更新即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int dp[200010][2][2];
const int inf=2e18+10;
signed main()
{
	int n,k;
	scanf("%lld%lld",&n,&k);
	//第二维 : 0表示最大值,1表示次大值
	//第三维:0表示颜色,1表示值
	for(int i=0;i<=k;i++)
	{
		dp[i][0][0]=-1;
		dp[i][0][1]=-inf;
		dp[i][1][0]=-1;
		dp[i][1][1]=-inf;
	}
	dp[0][0][0]=0,dp[0][0][1]=0;
	for(int i=1;i<=n;i++)
	{
		int c,v;
		scanf("%lld%lld",&c,&v);
		for(int j=k;j>=1;j--)
		{
			//保留
			if(dp[j][0][0]!=c) 
			{
				dp[j][0][0]=c;
				dp[j][0][1] += v;
			}
			else 
			{
				dp[j][0][0]=c;
				dp[j][0][1]=dp[j][1][1]+v;
			}
			//保留的时候已经用过上一层j的值了,后面不需要再用到了,而且不修改会影响判断
			//最大值和次大值对应的颜色不能相同,所以保留的情况已经更新过了,下面需要更新不保留的情况
			dp[j][1][0]=-1;
			dp[j][1][1]=-inf;
			for(int kk=0;kk<=1;kk++)
			{
				if(dp[j-1][kk][1]> dp[j][0][1])
				{
					if(dp[j-1][kk][0]!=dp[j][0][0])
					{
						dp[j][1][0]=dp[j][0][0];
						dp[j][1][1]=dp[j][0][1];
					}
					dp[j][0][0]=dp[j-1][kk][0];
					dp[j][0][1]=dp[j-1][kk][1];
				}
				else if(dp[j-1][kk][1]> dp[j][1][1]&&dp[j-1][kk][0]!=dp[j][0][0])
				{
					dp[j][1][0]=dp[j-1][kk][0];
					dp[j][1][1]=dp[j-1][kk][1];
				}
			}
		}
		if(dp[0][0][0]!=c) 
		{
			dp[0][0][0]=c;
			dp[0][0][1] += v;
		}
		else dp[0][0][1] = -inf;
	}
	if(dp[k][0][1]<0) printf("-1");
	else printf("%lld",dp[k][0][1]); 
	//和最长上升子序列有点像,但是最长上升子序列不知道删除个数,这题又限定了删除个数
}

F - Many Lamps(F - Many Lamps)

有n个灯泡和m条边,每条边连接两个灯,我们任意选边,然后将边上开着的灯关掉,关着的灯打开,问最后能否恰好有k个灯打开。

思路:我们考虑下操作实际的贡献:
00->11  +2
01->10 +0
10->01 +0
11->00 -2

所以变化要么是2,要么不变,那么就只有k为偶数才有答案,否则就无解。

那么具体该怎么解呢?我们来考虑开灯的顺序,假设选了1-2,又选了2-3,显然此时只有两个灯亮着,我们再选4-2,就有4个灯亮着。所以,我们来考虑一下,该怎么打开尽可能多的灯呢?图比较麻烦的话,我们去思考对一个树的情况。我们从叶子节点开始一层一层往上的话,开的灯会多一些,因为如果先开了父节点,那么开叶子节点的时候,父节点的状态就是不确定的了,但如果先开了子节点,那么用父节点的父节点去开的时候就不会影响子节点。

#include<bits/stdc++.h>
using namespace std;
int h[200010],e[400010],ne[400010],idx;
int st[200010],lamb[200010];
int n,m,k,cnt;
vector<int>q;
void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u,int fa)
{
	st[u]=1;
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		if(st[j]||j==fa) continue;
		dfs(j,u);
		if(lamb[j]==0&&cnt<k)
		{
			cnt -= lamb[j]+lamb[u];
			lamb[u]^=1,lamb[j]^=1;
			cnt += lamb[j]+lamb[u];
			//0 1
			q.push_back(i/2+1); 
		}
	}
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	memset(h,-1,sizeof h);
	for(int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v),add(v,u);
	}
	for(int i=1;i<=n;i++)
	{
		if(!st[i]) dfs(i,i);
	}
	if(cnt==k) 
	{
		printf("Yes\n");
		cout<<q.size()<<endl;
		for(auto it:q)
		{
			cout<<it<<" ";
		}
	}
	else printf("No\n");
}
  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码,好简单。(请点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞点赞)
AtCoder Beginner Contest 134 是一场 AtCoder 的入门级比赛,以下是每道题的简要题解: A - Dodecagon 题目描述:已知一个正十二边形的边长,求它的面积。 解题思路:正十二边形的内角为 $150^\circ$,因此可以将正十二边形拆分为 12 个等腰三角形,通过三角形面积公式计算面积即可。 B - Golden Apple 题目描述:有 $N$ 个苹果和 $D$ 个盘子,每个盘子最多可以装下 $2D+1$ 个苹果,求最少需要多少个盘子才能装下所有的苹果。 解题思路:每个盘子最多可以装下 $2D+1$ 个苹果,因此可以将苹果平均分配到每个盘子中,可以得到最少需要 $\lceil \frac{N}{2D+1} \rceil$ 个盘子。 C - Exception Handling 题目描述:给定一个长度为 $N$ 的整数序列 $a$,求除了第 $i$ 个数以外的最大值。 解题思路:可以使用两个变量 $m_1$ 和 $m_2$ 分别记录最大值和次大值。遍历整个序列,当当前数不是第 $i$ 个数时,更新最大值和次大值。因此,最后的结果应该是 $m_1$ 或 $m_2$ 中较小的一个。 D - Preparing Boxes 题目描述:有 $N$ 个盒子和 $M$ 个物品,第 $i$ 个盒子可以放入 $a_i$ 个物品,每个物品只能放在一个盒子中。现在需要将所有的物品放入盒子中,每次操作可以将一个盒子内的物品全部取出并分配到其他盒子中,求最少需要多少次操作才能完成任务。 解题思路:首先可以计算出所有盒子中物品的总数 $S$,然后判断是否存在一个盒子的物品数量大于 $\lceil \frac{S}{2} \rceil$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值