这是一个很好的题目,来自2013长春网赛。
题目的意思是给你2^N张扑克牌,每次洗牌前分别把从下开始数为奇数和偶数的牌分别拿出来放在一堆,两堆可以任意一个放在上面。
现在问你是否存在一种情况使得经过若干次洗牌后,第A张牌的编号为x,且同时第B张牌的编号为y。(初始状态从低到顶编号从1递增)
题目的解法是用二进制来模拟洗牌的过程,然后发现洗牌的规律为右移+异或操作,然后就可以解决了。
但其实本题最最难以想到的是,题目给你的牌的编号是从1开始的,然而为了方便地使用二进制,我们需要从0开始编号。
首先对于每一个牌,我们都先把其编号减一,相当于从0开始编号,这样对于读入的四个数,我们都将其减一就可以了。
然后我们需要在洗牌的过程中找出洗牌的规律。
是这样的,首先我们把奇数牌和偶数拍分别放成两堆,这样原来i为奇数的牌在奇数堆中间的编号就变为了(i/2),同理编号i为偶数的牌在偶数牌堆中的编号也是(i/2)。
由于要考虑两种牌分别放在上面的情况,我们先考虑奇数的牌放在上面的情况。
假设我们把所有的牌现有的位置用一个二进制表示,如果奇数的牌放在上面,那么偶数牌的最终编号就是偶数牌在偶数堆中间的编号,而奇数牌的编号需要把最高位由0变为1。
细细想来,把这用规律表示,这个规律就是如果奇数牌放下上面,那么现在牌的标号就是原来编号循环右移了一位。
好,下面讨论偶数牌放在上面的情况,其实是一样的,只是我们只在偶数牌的编号前面最高位由0变为1而已。
同样我们也用规律表示出来,那就是现在牌的编号为原来编号循环右移一位,并且改变最高位。
综合上面两种说法,如果是奇数牌放在上面,那么所有牌的编号就是循环右移一位,然后最高位异或0;如果是偶数牌放在上面,那么所有牌的编号就是循环右移一位,然后最高位异或1.
现在回到题目,如果x能够通过n次洗牌序号变为A,那么x一定右移了n位,并且每一个数位都与0或1做了异或操作。
由于每次洗牌两张牌都是做的同一个运算,也就是说x牌异或了1,那么y牌也一定异或了1。
那么我们只要枚举到底洗了多少次牌,然后看看对于每一位,x到A,y到B能否通过异或同一个值得到,如果所有的位数都是通过异或同一个值得到的,那么显然就存在这样的一个方案;否则,那就不存在这样的方案了。
下面还剩下最后一个问题,那就是我们到底应该判断多少位数呢?
最多只要n位,因为右移超过n位的话就是一个循环了,右移不会改变数位之间的对应关系。
第一次做这种二进制的题目,感觉题目的思路挺难想到的,虽然最终还是在内牛满面中A掉此题,但是如果比赛的时候出了这种类似的题目,依然无法保证能够A出来啊。。 T_T
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #define maxn 1111 5 using namespace std; 6 7 int a[maxn],b[maxn],x[maxn],y[maxn],f[maxn],n,k; 8 char s[maxn]; 9 bool ans; 10 11 void input(int t[]) 12 { 13 scanf("%s",s+1); 14 int N=strlen(s+1),cur=0,tep; 15 memset(f,0,sizeof f); 16 for (int i=1; i<=N; i++) f[i]=s[N+1-i]-'0'; 17 f[1]--; 18 for (int i=1; f[i]<0; ) f[i]+=10,f[++i]--; 19 while (N>=1) 20 { 21 tep=0; 22 t[++cur]=(f[1]&1); 23 for (int i=N; i>0; i--) 24 { 25 f[i]+=10*tep; 26 tep=(f[i]&1); 27 f[i]>>=1; 28 } 29 if (f[N]==0) N--; 30 } 31 } 32 33 int main() 34 { 35 int T; 36 scanf("%d",&T); 37 for (int cas=1; cas<=T; cas++) 38 { 39 scanf("%d",&n); 40 memset(a,0,sizeof a); 41 memset(x,0,sizeof x); 42 memset(b,0,sizeof b); 43 memset(y,0,sizeof y); 44 input(a),input(x),input(b),input(y); 45 for (int i=1; i<=n; i++)//右移了多少位 46 { 47 ans=true; 48 for (int j=1; j<=n; j++)//当前第j位进行比较 49 { 50 k=j-i;//低位在前,所以右移相当于减 51 if (k<1) k+=n; 52 if ((a[k]==x[j])^(b[k]==y[j])) 53 { 54 ans=false; 55 break; 56 } 57 } 58 if (ans) break;//找到了存在情况 59 } 60 printf("Case %d: ",cas); 61 if (ans) printf("Yes "); 62 else printf("No "); 63 } 64 return 0; 65 }