codeforces round 205 解题报告

ROUND 205 解题报告

A    Domino

关键字:构造

题目大意:

    给出牌的个数n(1<=n<=100),和每张牌牌的左右两边的两个不同的数字。我们可以对一张牌进行操作,交换其左右两边的数字。问,最少需要多少次交换,可以使得所有牌的左边数字之和、右边数字之和都为偶数。若不存在输出-1

思路:

    首先我们可以计算所有数字之和,若这个数字为奇数,那么一定不存在解。然后我们判断当前左右两边的数字之和是否都为偶数,即输出0的情况。排除这些后只剩下一种情况,就是左边和、右边和都为奇数。此时判断是否存在一张牌,两个数字一奇一偶,那么对这张牌进行操作就可以达到目标,即输出1。若找不到这样的牌,输出-1 

ROUND 205 解题报告

A    Domino

关键字:构造

题目大意:

    给出牌的个数n(1<=n<=100),和每张牌牌的左右两边的两个不同的数字。我们可以对一张牌进行操作,交换其左右两边的数字。问,最少需要多少次交换,可以使得所有牌的左边数字之和、右边数字之和都为偶数。若不存在输出-1

思路:

    首先我们可以计算所有数字之和,若这个数字为奇数,那么一定不存在解。然后我们判断当前左右两边的数字之和是否都为偶数,即输出0的情况。排除这些后只剩下一种情况,就是左边和、右边和都为奇数。此时判断是否存在一张牌,两个数字一奇一偶,那么对这张牌进行操作就可以达到目标,即输出1。若找不到这样的牌,输出-1 

时间复杂度:O(n)

空间复杂度:O(1)

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>

using namespace std;
int sum1,sum2,sum;
int flag1,flag2;
int main()
{
	int n;
	cin>>n;
	for (int i=1;i<=n;i++)
	{
		int x,y;
		cin>>x>>y;
		sum+=x+y;
		sum1+=x;
		sum2+=y;
		if (x%2==0&&y%2==1) flag1=1;
		if (y%2==0&&x%2==1) flag2=1;
	}
	if (sum&1)
	{
		cout<<-1<<endl;
		return 0;
	}
	if (sum1%2==0&&sum2%2==0)
	{
		cout<<0<<endl;
		return 0;
	}
	if (flag1||flag2)  cout<<1<<endl;
	             else  cout<<-1<<endl;
	return 0;
}
		


B    Two Heaps

关键字:组合数学 构造

题目大意:

    给定2n10-99的数,要求将其分成两组,每组数字的个数都为n。现在我们用这两个数字拼四位数。用第一组中的一个数作为四位数的前两位,用第二组中的一个数作为四位数的后两位。现在要求一种将2n个数的分成两组的方法,使得按照上述方式能构造的不同的四位数最多。

思路:

    我们知道,每种数字的前两次出现是有效的。即我们将这两个数字分别分到两个组里。如果这个数出现了第三次,我们可以把它当成“辣鸡”。即我们将其放到任意一组都不影响结果。那么我们按第一组、第二组、第一组······的顺序,将每个数字的前两次出现依次填入。之后我们将剩余的数字依次填入即可。

时间复杂度:O(n)

空间复杂度:O(n)

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>

using namespace std;

struct node
{
	int data,pos;
}a[210];
int cmp(node a,node b)
{
	return a.data<b.data;
}
int pp[100],way[210],num=0,p1=1,p2=2;
int main()
{
	int n;
	cin>>n;
	for (int i=1;i<=2*n;i++)
	{
		int x;
		cin>>a[i].data;
		a[i].pos=i;
	}
	sort(a+1,a+2*n+1,cmp);
	for (int i=1;i<=2*n;i++)
	{
		if (pp[a[i].data]<=1)
		{
			pp[a[i].data]++;
			way[a[i].pos]=p1;
			p1=3-p1;
			num++;
		}
		else
		{
			way[a[i].pos]=p2;
			p2=3-p2;
		}
	}
	cout<<(num/2)*(num-num/2)<<endl;
	for (int i=1;i<=2*n;i++)
	{
		printf("%d",way[i]);
		if (i!=2*n) printf(" ");
		       else printf("\n");
    }	
	return 0;
}
	


C    Find Maximum

关键字:模拟

题目大意:

    给定n个系数,和一个函数f(x)x是一个位数不超过n的二进制的数。那么f(x)的值就等于x中所有的1所对应的系数ai的和。给定一个二进制数m,要求你是输出f(x)的最大值,其中0<=x<=m

思路:

    很经典的二进制数字的处理办法。首先我们计算出f(m)的值。然后,我们从后往前找每一个是1的数,考虑将其变为0,将其后面的所有数都设为1的对应的数的f的值。我们只要取其中的最大值即可。

时间复杂度:O(n)

空间复杂度:O(n)

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>

using namespace std;

int main()
{
	int n;
	int a[110000];
	cin>>n;
	for (int i=0;i<n;i++)
	    cin>>a[i];
	string s;
	cin>>s;
	int maxx=0,now=0;
	for (int i=0;i<n;i++)
	    if (s[i]=='1') now+=a[i];
	maxx=now;
	int sum=0;
	for (int i=0;i<n;i++)
	{
		if (s[i]-'0'==1)
		{
			now=now-a[i]+sum;
			maxx=max(maxx,now);
			sum=a[i];
		}
		else sum+=a[i];
	}
	cout<<maxx<<endl;
    return 0;
}
		


D    Queue

关键字:构造 贪心

题目大意:

    给定一个由MF构成的长度为n的序列s。每过一秒,所有满足下列关系的相邻两项交换:s[i]=M,s[i+1]=F(1<=i<=n-1)。问经过多少时间,所有F都在所有M之前。

思路:

    一个很有趣的贪心思路。首先我们能想到,这个时间一定是序列中最后一个F交换到前面所花的时间。我们设上一个不在正确位置上的F需要last秒交换到正确位置,设当前这个F离正确位置的距离为x。那么当前这个F交换到正确位置的时间是max(last+1,x)。为什么是正确的呢?我们知道,当前这个F交换到正确位置只有两种情况,第一种是一路畅通无阻的交换过去,即x秒。第二种则是因为它之前的一个F尚未交换,“挡到了”当前这个F。而当这个F“前进后”会给当前这个F一个M用于交换,所以这个F所用的时间不会超过前一个F的时间+1 。所以我们在扫完一遍后,输出last的值即可。

时间复杂度:O(n)

空间复杂度:O(n)

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>

using namespace std;

int main()
{
	int last=0,num=0;
	string s;
	cin>>s;
	int n=s.size();
	for (int i=0;i<n;i++)
	{
		if (s[i]=='F')
		{
			if (i!=num) last=max(i-num,last+1);
			num++;
		}
	}
	cout<<last<<endl;
	return 0;
}


E    Antichain

关键字:构造

题目大意:

    图Gn个点,分别标号为0n-1。图Gn条有向边,连接着i(i+1)mod n。题目输入为长度为n01串,第i位如果为0表示i(i+1)mod n这条有向边的方向为i(i+1)mod n。如果第i位为1,则方向为(i+1)mod ni。题目要求你找出一个最大的点集,对于这个点集中的任意两个点uv,图G中都不存在<u,v><v,u>这样的边。要求输出这个最大的点集的大小。

思路:

    我们把连续的、方向相同的一些边称为一条链。如果链的长度大于等于2,那么我们可以选取链中间的一点最优。如果链的长度为1,比如1=>2<=3=>4<=5。我们可以选择1 3 5这三个点。

时间复杂度:O(n)

空间复杂度:O(n) 

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>

using namespace std;

int main()
{
	string s;
	cin>>s;
	int ans=0;
	int num=s.size();
	if (num==2)
	{
		cout<<1<<endl;
		return 0;
	}
	s+=s;
	for (int i=0;i<num;i++)
	{
		if (s[i]!=s[i+1])
		{
			if (s[i]!=s[i+2]) ans++;
			else if (s[i]!=s[i+3]) ans++,i++;
		}
	}
	cout<<ans<<endl;
	return 0;
}


 

 

 

 

 

 

 

 


B    Two Heaps

关键字:组合数学 构造

题目大意:

    给定2n10-99的数,要求将其分成两组,每组数字的个数都为n。现在我们用这两个数字拼四位数。用第一组中的一个数作为四位数的前两位,用第二组中的一个数作为四位数的后两位。现在要求一种将2n个数的分成两组的方法,使得按照上述方式能构造的不同的四位数最多。

思路:

    我们知道,每种数字的前两次出现是有效的。即我们将这两个数字分别分到两个组里。如果这个数出现了第三次,我们可以把它当成“辣鸡”。即我们将其放到任意一组都不影响结果。那么我们按第一组、第二组、第一组······的顺序,将每个数字的前两次出现依次填入。之后我们将剩余的数字依次填入即可。

时间复杂度:O(n)

空间复杂度:O(n)

C    Find Maximum

关键字:模拟

题目大意:

    给定n个系数,和一个函数f(x)x是一个位数不超过n的二进制的数。那么f(x)的值就等于x中所有的1所对应的系数ai的和。给定一个二进制数m,要求你是输出f(x)的最大值,其中0<=x<=m

思路:

    很经典的二进制数字的处理办法。首先我们计算出f(m)的值。然后,我们从后往前找每一个是1的数,考虑将其变为0,将其后面的所有数都设为1的对应的数的f的值。我们只要取其中的最大值即可。

时间复杂度:O(n)

空间复杂度:O(n)

D    Queue

关键字:构造 贪心

题目大意:

    给定一个由MF构成的长度为n的序列s。每过一秒,所有满足下列关系的相邻两项交换:s[i]=M,s[i+1]=F(1<=i<=n-1)。问经过多少时间,所有F都在所有M之前。

思路:

    一个很有趣的贪心思路。首先我们能想到,这个时间一定是序列中最后一个F交换到前面所花的时间。我们设上一个不在正确位置上的F需要last秒交换到正确位置,设当前这个F离正确位置的距离为x。那么当前这个F交换到正确位置的时间是max(last+1,x)。为什么是正确的呢?我们知道,当前这个F交换到正确位置只有两种情况,第一种是一路畅通无阻的交换过去,即x秒。第二种则是因为它之前的一个F尚未交换,“挡到了”当前这个F。而当这个F“前进后”会给当前这个F一个M用于交换,所以这个F所用的时间不会超过前一个F的时间+1 。所以我们在扫完一遍后,输出last的值即可。

时间复杂度:O(n)

空间复杂度:O(n)

E    Antichain

关键字:构造

题目大意:

    图Gn个点,分别标号为0n-1。图Gn条有向边,连接着i(i+1)mod n。题目输入为长度为n01串,第i位如果为0表示i(i+1)mod n这条有向边的方向为i(i+1)mod n。如果第i位为1,则方向为(i+1)mod ni。题目要求你找出一个最大的点集,对于这个点集中的任意两个点uv,图G中都不存在<u,v><v,u>这样的边。要求输出这个最大的点集的大小。

思路:

    我们把连续的、方向相同的一些边称为一条链。如果链的长度大于等于2,那么我们可以选取链中间的一点最优。如果链的长度为1,比如1=>2<=3=>4<=5。我们可以选择1 3 5这三个点。

时间复杂度:O(n)

空间复杂度:O(n) 

 

 

 

 

 

 

 

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值