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)