Educational Codeforces Round 141 (Rated for Div. 2) A ~ C 题解

文章提供了三道算法题的解题思路和代码实现,涉及数组的特殊排列,使得每个数不等于其前面所有数字之和,矩阵的构造以最大化相邻数差值种类,以及在有限时间内优化玩家战斗排名。解题策略包括降序排列、交替蛇形摆放和贪心算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A. Make it Beautiful

题意

题目链接
给定一个长度为 n n n的数组,将该数组重新排列使得数组内的每一个数都不等于其前面的所有数字之和。

思路

首先想到的应该是降序排列,因为所有数都是大于 0 0 0的,但是因为存在重复的数,比如 3366 3366 3366这组样例,最大的数数量超过一个,就导致第二个数不满足要求。那么可以拿一个不等于这大数的其他数放在数组最前面,这样就可以避免这种情况了。否则如果这个数列中所有数都相同,就拿不出来不同的数放前面,就构造不出来。

注意这个题不能关输出同步流,否则 p u t s puts puts函数输出的结果就无法与 c o u t cout cout函数输出的结果同步。当输出函数只有 c o u t cout cout时才可以关输出同步流。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 50 + 5;
ll a[maxn];
int main(){
	//ios::sync_with_stdio(0);
	//cin.tie(0),cout.tie(0);
	ll t;
	cin>>t;
	while(t--){
		ll n;
		cin>>n;
		for(ll i=1;i<=n;i++)cin>>a[i];
		sort(a+1,a+1+n,greater<ll>());
		bool flag=false;
		for(ll i=2;i<=n;i++){
			if(a[i]!=a[1]){
			    flag=true;
			    break;
			}
		}
		if(flag==false)puts("NO");
		else{
			puts("YES");
			if(a[1]==a[2]){
				ll x,pos;
				for(ll i=3;i<=n;i++){
					if(a[i]!=a[1]){
						x=a[i];
						pos=i;
						break;
					}
				}
				cout<<x<<" ";
				for(ll i=1;i<=n;i++){
					if(pos==i)continue;
					cout<<a[i]<<" ";
				}
				cout<<endl;
			}
			else{
				for(ll i=1;i<=n;i++)cout<<a[i]<<" ";
				cout<<endl;
			}
		}
	}
    return 0;
}

B. Matrix of Differences

题意

题目链接
请构造一个长宽为 n n n的矩阵,要求矩阵内所有的数字是 [ 1 , n 2 ] [1,n^2] [1,n2]的排列,且要求矩阵内每两个相邻数之间的绝对值之差的种类数量最多,输出该矩阵。

思路

[ 1 , n 2 ] [1,n^2] [1,n2]按照一当前极小一当前极大的顺序交替蛇形摆放。这其实是一种极值减幅振荡,差值趋近于零的思想。因为只有这么构造,才能给出种类数为 n 2 − 1 n^2-1 n21的最大种类数矩阵。按照蛇形差值的方式,横向有 n ( n − 1 ) = n 2 − n n(n-1)=n^2-n n(n1)=n2n对差值,纵向有 n − 1 n-1 n1对差值,一共有 n 2 − n + n − 1 = n 2 − 1 n^2-n+n-1=n^2-1 n2n+n1=n21对差值,刚好构造出最大种类数矩阵。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 50 + 5;
ll a[maxn][maxn];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	ll t;
	cin>>t;
	while(t--){
		ll n;
		cin>>n;
		ll curx = 1, cury = 1;
		ll first = 1, last = n*n;
		ll row = 1;
		for(ll i=1;i<=n*n;i++){
			if(i&1)a[curx][cury]=first,first++;
			else a[curx][cury]=last,last--;
			if(row&1){
				cury++;
				if(cury==n+1)cury=n,row++;
			}
			else{
				cury--;
				if(cury==0)cury=1,row++;
			}
			if(i%n==0)curx++;
		}
		for(ll i=1;i<=n;i++){
			for(ll j=1;j<=n;j++){
				cout<<a[i][j]<<" ";
			}
			cout<<endl;
		}
	}
    return 0;
}

C. Yet Another Tournament

题意

题目链接
n n n名玩家,战斗力分别为 [ 1 , n ] [1,n] [1,n],我们也是一名玩家,一共有 n + 1 n+1 n+1名玩家。对于 n n n名玩家来说, i i i能击杀 j j j当且仅当 i > j i>j i>j。对于每一位玩家我们都有一个准备值 a i a_{i} ai,我们想要击败玩家 i i i需要花费 a i a_{i} ai的时间去准备。刚开始我们拥有的时间为 m m m,每名玩家都和其余所有玩家进行一轮对局,最终按照胜场数量对所有玩家进行排名,求我们合理分配时间会获得的最高排名。

思路

已知对于玩家 [ 1 , n ] [1,n] [1,n],他们的基础胜场是 [ 0 , n − 1 ] [0,n-1] [0,n1],假如我们的胜场是 x x x,那么我们就至少能够击败或打平前 x x x名玩家,因为第 x x x名玩家的基础胜场只有 x − 1 x-1 x1,就算把我们击败了,他的胜场也只能和我们相同。然而,对于第 x + 1 x+1 x+1名玩家,他的基础胜场是 x x x,如果他击败了我们,那么他的胜场就超过了我们。因此,只有我们赢了他,我们才不会被他超过,排名才能尽量靠前。

在有限的时间内,我们自己一定会尽量击杀更多的对手。因此我们要在保证胜场的前提下去贪心。我们先计算出最多能够击杀多少玩家,然后判断这些玩家中能否包括第 x + 1 x+1 x+1名玩家,如果包括了,我们的排名就能够相对靠前。

因此,我们先按照 a i a_{i} ai从小到大进行结构体排序,计算胜利场次 x x x最多是多少,然后标记所有可能被击杀的玩家,判断这些玩家中有无第 x + 1 x+1 x+1名玩家。

如果没有,则答案为: n + 1 − x n+1-x n+1x;
如果有,则答案为: ( n + 1 − x ) − 1 = n − x (n+1-x)-1=n-x (n+1x)1=nx
即: a n s = n − x + ( v i s [ x + 1 ] = = f a l s e ) ans=n-x+(vis[x+1]==false) ans=nx+(vis[x+1]==false)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 5e5 + 5;
struct node{
	ll id, time;
	bool friend operator < (const node A, const node B){
		return A.time < B.time;
	}
}a[maxn];
bool vis[maxn];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	ll t;
	cin>>t;
	while(t--){
		ll n,m;
		cin>>n>>m;
		memset(vis,0,sizeof(vis));
		for(ll i=1;i<=n;i++)cin>>a[i].time,a[i].id=i;
		sort(a+1,a+1+n);
		ll cur = m, x = 0;
		for(ll i=1;i<=n;i++){
			if(cur-a[i].time>=0){
				cur-=a[i].time;
				x++;
				vis[a[i].id]=true;
			}
			else{
				cur+=a[i-1].time;
				if(cur-a[i].time>=0){
					cur-=a[i].time;
					vis[a[i].id]=true;
				}
				else break;
			}
		}
		ll ans = n-x+(vis[x+1]==false);
		cout<<ans<<endl;
	}
    return 0;
}

心得

这次又是只差一点就能出C题了,思路差不多,就是时间不够,如何判断第 x + 1 x+1 x+1名玩家是否能够被击败这一步,是比较难快速想到的。其思想是,找到所有可能的被击败的玩家,而不是找完当前的就不找了。这是一大坑点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

keguaiguai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值