Codeforces:Educational Round 109 (Rated for Div. 2) ABCD题解(c++实现)

比赛的记录。

废话不多说。

-----------------------------------------------------------------------------

A. Potion-making

链接:Problem - A - Codeforces

思路:配魔法药水,要求魔法精华的浓度为k。t组样例,每组样例给你k值,输出要配出浓度的为k的魔法药水的魔药和水的量的最小和。

就输出 k + (100-k) ,如果k和(100-k)不互质就同除以最大公约数。

代码:

#include<bits/stdc++.h> 
#define endl '\n'
#define ll long long
using namespace std;
int gcd(int x,int y){
	return y?gcd(y,x%y):x;
}
int main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int t;cin>>t;
	while(t--){
		int k;cin>>k;
		int water=100-k; 
		int cho=gcd(water,k);
		if(cho!=1){
			water/=cho;k/=cho;
		}
		cout<<water+k<<endl;
	} 
}

-----------------------------------------------------------------------------

B. Permutation Sort

链接:Problem - B - Codeforces

思路:给你一个数组,要求你给这个数组从小到大排序,每次可以选k个元素排序,注意k不能等于n,然后问你最少的排序次数是多少次。

既然不能给n个元素直接排序,我们可以给n-1个元素排序。

给 1 到 n-1 的元素排序 。排完后,如果 第 n 个元素 大于 排序完成后 第 n-1 个元素,那么这个数组就排完了 ,次数为1。如果 第 n 个元素小于排序后的第 n-1 个元素但是大于第 1 个元素,那么对 2到 n再排序一次,数组就排完了,这时次数为2。最后一种情况,如果第 n 个元素小于排序后的第 n-1 个元素并且小于第 1 个元素,这时给2到n排序一次,再给 1 到 n-1 排序一次,数组就完成了,这时次数为3。

同理按照数组对称性,我们一开始是给 1 到 n-1 排序的,也可以是给 2 到 n 排序,这时按照上面的做法,检查第 1 个元素 值的大小就可以了。

选取这2种方案中次数最小的方案。

代码:

#include<bits/stdc++.h> 
#define endl '\n'
#define ll long long
using namespace std;
const int N=55;
int ar[N];
int main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int t;cin>>t;
    while(t--){
    	memset(ar,0,sizeof(ar));
    	int n;cin>>n;
    	int ok=0;
		int fro_max=-1,end_min=0x3f3f3f3f;
		int fro_min=0x3f3f3f3f,end_max=-1;
    	for(int i=1;i<=n;i++){
    		cin>>ar[i];
            if(ar[i]<ar[i-1])ok=1;
            if(i>1)end_min=min(end_min,ar[i]),end_max=max(end_max,ar[i]);
            if(i<n)fro_max=max(fro_max,ar[i]),fro_min=min(fro_min,ar[i]);
		}
		if(!ok)cout<<0<<endl;
		else{
		    if(fro_max<=ar[n]||end_min>=ar[1])cout<<1<<endl;
		    else{
		    	if(fro_min>ar[n]&&end_max<ar[1])cout<<3<<endl;
		    	else cout<<2<<endl;
			}
		}
	}
}

-----------------------------------------------------------------------------

C. Robot Collisions

链接:Problem - C - Codeforces

没写出来,我看别的大佬的思路学会的。

佬的链接:(31条消息) Educational Codeforces Round 109 (Rated for Div. 2)C. Robot Collisions D. Armchairs题解_solemntee的博客-CSDN博客

思路:在一段长度有限的坐标轴上,有n只蚂蚁,它们起始分别在不同的点上,每只蚂蚁最开始都有一个起始的运动方向,如果碰到墙壁就会往回走,每秒走一步。

如果两个蚂蚁在整数点相遇,它们两个就会爆炸。然后就没了。要求你输出每只蚂蚁在什么时候爆炸,如果不会爆炸就输出-1。

注意,上面的蚂蚁只会在整数点相遇才会爆炸其实让这题变简单了。 如果一只蚂蚁的起始点是偶数,那么可以由简单的推导得知,它只会与同样起始点是偶数的蚂蚁相遇。同理,奇数也只会遇见奇数。因此可以把数组中奇数和偶数的蚂蚁分开处理。

难点就是怎么处理蚂蚁爆炸的时间。

假设现在有3只蚂蚁X、Y、Z。它们一开始都在偶数(或者奇数)点上。从左往右看,如果X一开始向右走,Y一开始向左走,那么Y一定先和X相撞,无论Z向什么方向走。如果一开始X向左走,Y也向左走,那么那么Y也一定先和X相撞,无论Z向什么方向走。但是如果不是上面这两种情况,比如X向左,Y向右走,那么Y有可能先和Z相撞,也有可能先和X相撞,这取决Z的方向。

按照这个思路走,用一个stack来存蚂蚁,如果stack顶部的蚂蚁和现在的蚂蚁是前两种情况, 那么让stack顶部的蚂蚁出栈,现在的蚂蚁不入栈,就算是这两只蚂蚁爆炸了。否则,就让现在的蚂蚁入栈,等之后再处理这只蚂蚁。

同理,奇数蚂蚁和偶数蚂蚁分开来一遍这个流程就行了。

如果理解了这题的想法,就发现这题其实不难,但是挺难写的(不难,但是难)。

代码:

#include<bits/stdc++.h> 
#define endl '\n'
#define ll long long
using namespace std;
const int N=3e5+7;
struct node{
	int id,dis,dir;
	//结构体内嵌比较函数 
	bool operator <(const node &tmp)const{
		return dis<tmp.dis;
	}
}ar[N];
// stack,even用来存偶数蚂蚁,odd用来存奇数蚂蚁 
stack<node> even,odd; 
int cnt[N];   // cnt用来存蚂蚁爆炸时间 
int main()
{
	ios::sync_with_stdio(false);
    int t;cin>>t;
    while(t--){
    	int n,m;cin>>n>>m;
    	if(!even.empty())even.pop();
    	if(!odd.empty())odd.pop();
    	for(int i=1;i<=n;i++){
    		cin>>ar[i].dis;
    		ar[i].id=i;
		}
		for(int i=1;i<=n;i++){
			char ch[2];cin>>ch;
			if(ch[0]=='L')ar[i].dir=1;   // dir为1表示这只蚂蚁向左 
			else if(ch[0]=='R')ar[i].dir=0;   //  否则表示向右走 
		}
		sort(ar+1,ar+1+n);
		memset(cnt,-1,sizeof(cnt));   //不会爆炸就输出-1 
		for(int i=1;i<=n;i++){
			if((ar[i].dis)%2==1){
				if(odd.empty())odd.push(ar[i]);
				else{
					node tmp=odd.top();
					// 第一种情况,两只相邻的蚂蚁都向左走 
					if(tmp.dir==1&&ar[i].dir==1){
						cnt[tmp.id]=tmp.dis+abs(tmp.dis-ar[i].dis)/2;
						cnt[ar[i].id]=cnt[tmp.id];
						odd.pop();
					}
					// 第二种情况,一只向右,一只向左 
					else if(tmp.dir==0&&ar[i].dir==1){
						cnt[tmp.id]=abs(tmp.dis-ar[i].dis)/2;
						cnt[ar[i].id]=cnt[tmp.id];
						odd.pop();
					}
					// 两种都不是的话,就把这只蚂蚁后来再处理 
					else{
						odd.push(ar[i]);
					}
				}
			}
			if((ar[i].dis)%2==0){
				if(even.empty())even.push(ar[i]);
				else{
					node tmp=even.top();
					if(tmp.dir==1&&ar[i].dir==1){
						cnt[tmp.id]=tmp.dis+abs(tmp.dis-ar[i].dis)/2;
						cnt[ar[i].id]=cnt[tmp.id];
						even.pop();
					}
					else if(tmp.dir==0&&ar[i].dir==1){
						cnt[tmp.id]=abs(tmp.dis-ar[i].dis)/2;
						cnt[ar[i].id]=cnt[tmp.id];
						even.pop();
					}
					else{
						even.push(ar[i]);
					}
				}
			}
		}
		//如果栈里还有两只或以上的蚂蚁,就处理。 
		while(odd.size()>=2){
			node next=odd.top();odd.pop();
			node fro=odd.top();odd.pop();
			if(next.dir==0&&fro.dir==0){
				cnt[next.id]=(m-next.dis)+abs(next.dis-fro.dis)/2;
				cnt[fro.id]=cnt[next.id];
			}
			else if(next.dir==0&&fro.dir==1){
				cnt[next.id]=((m-next.dis)+fro.dis+m)/2;
				cnt[fro.id]=cnt[next.id];
			}
		}
		while(even.size()>=2){
			node next=even.top();even.pop();
			node fro=even.top();even.pop();
			if(next.dir==0&&fro.dir==0){
				cnt[next.id]=(m-next.dis)+abs(next.dis-fro.dis)/2;
				cnt[fro.id]=cnt[next.id];
			}
			else if(next.dir==0&&fro.dir==1){
				cnt[next.id]=((m-next.dis)+fro.dis+m)/2;
				cnt[fro.id]=cnt[next.id];
			}
		}
		for(int i=1;i<=n;i++)cout<<cnt[i]<<' ';
		cout<<endl; 
	}
}

-----------------------------------------------------------------------------

D. Armchairs

链接:Problem - D - Codeforces

一样不会,看大佬的。

我找不到那位佬的链接了,sorry。

思路:给你一个元素为0或1的有n个元素的数组,要求把这个数组上的1移到0的位置。保证1的元素数量小于n/2。

求把每个1移走的最小步骤的和。

分别把每个1、0用两个数组存下它们的位置,然后dp。

dp[ i ] [ j ] 表示把前 i 个1 移到 前 j 个0 所用的最小步骤。

可以得到递推式为:dp[ i ][ j ]=min(dp[ i ][ j-1 ],dp[ i-1 ][ j-1 ]+abs( hash_1[ i ] - hash_0[ j ]) )。

hash_1 表示了元素1的位置 , hash_0 表示了元素0的位置。

可以这样理解,要么把第 i 个 1 移到第 j 个0 上,要么就不移动保持不变,选取移动数小的方案。

注意如果 i 为1 时,这个式子有点小小的改变:dp[ i ][ j ]=min( dp[ i ][ j-1 ],abs(hash_1[ i ] - hash_0[ j ]) )

代码:

#include<bits/stdc++.h> 
#define endl '\n'
#define ll long long
using namespace std;
const int N=5500;
int tot_1,tot_0;
int hash_1[N],hash_0[N],dp[N][N];
int main()
{
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 
    int n;cin>>n;
    for(int i=1;i<=n;i++){
    	int x;cin>>x;
    	if(x==1)hash_1[++tot_1]=i;
    	else hash_0[++tot_0]=i;
	}
	if(tot_1==0)cout<<0<<endl;
	else{
		memset(dp,0x3f3f3f3f,sizeof(dp));
		for(int i=1;i<=tot_1;i++){
			for(int j=i;j<=tot_0;j++){
				if(i==1)dp[i][j]=min(dp[i][j-1],abs(hash_1[i]-hash_0[j]));
				else dp[i][j]=min(dp[i][j-1],dp[i-1][j-1]+abs(hash_1[i]-hash_0[j]));
			}
		}
		cout<<dp[tot_1][tot_0]<<endl;
	}
}

-----------------------------------------------------------------------------

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值