CodeForces Round #715(div. 2) A-D 题解

A 题目链接:Problem - A - Codeforces

input:

4
3
1 1 2
3
1 1 1
8
10 9 13 15 3 16 9 13
2
18 9

output:

1 1 2 
1 1 1 
13 9 13 15 3 9 16 10 
9 18

题意:给出n个整数,按一定顺序排列,使得每相邻两个数ai,ai+1,,满足(ai + ai+1)/ 2是整数的数对最多

策略:一道水题,只需先输出所有奇数,再输出所有偶数即可~~

代码如下: 

#include<bits/stdc++.h>
using namespace std;
const int N=2005;
 
int t,n;
int a[2005];
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>n;
		for(int i=1;i<=n;i++)
			cin>>a[i];
		for(int i=1;i<=n;i++)
		{
			if(a[i]%2==1)
				cout<<a[i]<<" ";
		}
		for(int i=1;i<=n;i++)
		{
			if(a[i]%2==0)
				cout<<a[i]<<" ";
		}
		cout<<endl;
	}
	return 0;
}

B 题目链接:Problem - B - Codeforces

 input:

5
3
TMT
3
MTT
6
TMTMTT
6
TMTTTT
6
TTMMTT

output:

YES
NO
YES
NO
YES

题意:给定一个由T和M组成且长度为n的串,问能否将其拆分成n/3个”TMT“子序列

策略:赛后搜索题解,发现很多大佬提出了括号匹配,现在想想确实很像,我的做法是先用两个前缀和数组,遍历一遍串,分别记录M和T的前缀和,然后保证每到M时,当前M的数量始终不大于T。然后再memset一下,用后缀和反向遍历一次,做法同上。

另外注意加一下特判,一定是T数量是M的2倍时,才有可能成功拆分,否则直接输出NO。

代码如下(略长,属实拉跨了,不如同队俩哥们写的好看)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
 
int t,n;
int am[N],at[N];	//M和T的前缀和数组 
 
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>n;
		string s;
		cin>>s;
		memset(am,0,sizeof am);
		memset(at,0,sizeof at);
		for(int i=0;i<n;i++)	//计算前缀和 
		{
			if(s[i]=='M')
			{
				am[i]=am[i-1]+1;
				at[i]=at[i-1]; 
			}
			else
			{
				am[i]=am[i-1];
				at[i]=at[i-1]+1;				
			}
		}
		if(am[n-1]*2!=at[n-1])	//特判 
		{
			cout<<"NO"<<endl;
			continue;
		}
		int flag=0;
		for(int i=0;i<n;i++)	//遍历 
		{
			if(s[i]=='M'&&am[i]>at[i])
				flag=1;
			else
				continue;
		}
		if(flag==1)
		{
			cout<<"NO"<<endl;
			continue;			
		}
		memset(am,0,sizeof am);
		memset(at,0,sizeof at);
		for(int i=n-1;i>=0;i--)	//后缀和 
		{
			if(s[i]=='M')
			{
				am[i]=am[i+1]+1;
				at[i]=at[i+1]; 
			}
			else
			{
				am[i]=am[i+1];
				at[i]=at[i+1]+1; 				
			}			
		}
		flag=0;
		for(int i=n-1;i>0;i--)	//遍历 
		{
			if(s[i]=='M'&&am[i]>at[i])
				flag=1;
			else
				continue;
		}
		if(flag==1)
		{
			cout<<"NO"<<endl;
			continue;
		}
		cout<<"YES"<<endl; 
	}
	return 0;
}

C 题目链接:Problem - C - Codeforces

input1:

3
3 1 2

 output1:
 

3

input2:

1
5

output2:

0

input3:

6
1 6 3 3 6 3

output3:

11

input4:
 

6
104 943872923 6589 889921234 1000000000 69

output4:

2833800505


题意:有n个人,第i个人的速度为si,di为前i个人中最大速度和最小速度的差值,将这n个人按一定顺序排列,使得d1+d2+……+dn的值最小,输出该最小值

策略:读题后想到了区间dp,于是先套了一个区间dp的板子进去,状态转移方程没仔细研究,按着自己的推测整了一个: dp[i][j] = min(dp[i+1][j]+a[i],dp[i][j-1]+a[j]);发现样例4和答案比较接近,于是推测dp的大方向是对的。

赛时 @与风做友 同学提出了更新区间最大值和最小值的想法,我当即有了思路,既然区间dp最终得到的结果是一定的,并且不要求初始排列的顺序固定,那直接sort一下就ok了,这样可以保证每个区间的最左端是最小值,最右端是最大值。

同时发现了该题的规律:区间中最后一个d一定是该区间最大值和最小值的差,于是得出下面的状态转移方程: dp[i][j] = min(dp[i + 1][j] , dp[i][j - 1]) + a[j] - a[i]

修改完以后,就华丽地AC了~

代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;    //必开long long
const int N=2005;
 
ll a[N];    //速度数组
ll dp[N][N];    //dp[i][j]代表[i,j]区间中该问题的最小值
int n;
 
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	sort(a+1,a+n+1);    //快排
	for(int r=2;r<=n;r++)    //区间长度
	{
		for(int i=1;i<=n-r+1;i++)
		{
			int j=i+r-1;
			if(r!=2)
				dp[i][j]=min(dp[i+1][j],dp[i][j-1])+a[j]-a[i];    //状态转移方程
			else
				dp[i][j]=a[j]-a[i];	    //r==2时特判
		}	
	}
	cout<<dp[1][n]<<endl;
	return 0;
}

D 题目链接:Problem - D - Codeforces

 input:
 

2
1
00
11
01
3
011001
111010
010001

output:

010
011001010

题意:给定一个数字n,并给出3个长度为2n的01串,要求构造一个长度为3n的01串,使至少两个先前给的串是该串的子序列

策略:赛时搞了好久,最后都WA了,队伍整体思路有些问题,现在给出补题的思路。

三个串各设置一个指针,因为串只有0和1,所以必定会至少有2个串当前的指针对应的值相等。将这个值插入答案串中,相等的串上两个指针右移。重复该过程,直至其中一个串遍历完成。

此时我们可以sort一下三个指针的值,最大的那个值,说明其代表的串全部构造完成,此时我们找到指针值排第二的那个,该串构造度最高,所以直接把后面尚未构造的部分直接插入进答案串中。

然后答案串0~3n-1输出一下即可

代码如下:

#include<bits/stdc++.h>
using namespace std;

const int N=3E5+5;	//注意数据量 
int t,n;
string a,b,c;	//三个串 
int s[N];	//答案串 
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>n;
		cin>>a>>b>>c;
		int i=0,j=0,k=0,l=0;
		while(i<a.length()&&j<b.length()&&k<c.length())	//构造 
		{
			if(a[i]==b[j])
			{
				s[l]=a[i]-48;
				++i;
				++j;
				++l;
			}
			else if(b[j]==c[k])
			{
				s[l]=b[j]-48;
				++j;
				++k;
				++l;
			}
			else 
			{
				s[l]=a[i]-48;
				++i;
				++k;
				++l;
			}				
		}
		int so[3]={i,j,k};
		sort(so,so+3);	//p排序三个指针值,找中间那个,其代表的串剩余部分加入答案 
		if(so[1]==i)
		{
			for(i;i<2*n;i++)
			{
				s[l]=a[i]-48;
				++l;
			}
		}
		else if(so[1]==j)
		{
			for(j;j<2*n;j++)
			{
				s[l]=b[j]-48;
				++l;
			}
		}
		else
		{
			for(k;k<2*n;k++)
			{
				s[l]=c[k]-48;
				++l;
			}
		}
		for(int p=0;p<3*n;p++)
			cout<<s[p];
		cout<<endl;		
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值