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
关键字:组合数学 构造
题目大意:
给定2n个10-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
关键字:构造
题目大意:
图G有n个点,分别标号为0到n-1。图G有n条有向边,连接着i与(i+1)mod n。题目输入为长度为n的01串,第i位如果为0表示i与(i+1)mod n这条有向边的方向为i到(i+1)mod n。如果第i位为1,则方向为(i+1)mod n到i。题目要求你找出一个最大的点集,对于这个点集中的任意两个点u,v,图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
关键字:组合数学 构造
题目大意:
给定2n个10-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
关键字:构造
题目大意:
图G有n个点,分别标号为0到n-1。图G有n条有向边,连接着i与(i+1)mod n。题目输入为长度为n的01串,第i位如果为0表示i与(i+1)mod n这条有向边的方向为i到(i+1)mod n。如果第i位为1,则方向为(i+1)mod n到i。题目要求你找出一个最大的点集,对于这个点集中的任意两个点u,v,图G中都不存在<u,v>或<v,u>这样的边。要求输出这个最大的点集的大小。
思路:
我们把连续的、方向相同的一些边称为一条链。如果链的长度大于等于2,那么我们可以选取链中间的一点最优。如果链的长度为1,比如1=>2<=3=>4<=5。我们可以选择1 3 5这三个点。
时间复杂度:O(n)
空间复杂度:O(n)