现在的位置: 首页 > 综合 > 正文

二进制-hdu-4759-Poker Shuffle

2013年12月07日 ⁄ 综合 ⁄ 共 1584字 ⁄ 字号 评论关闭

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=4759

题目大意:

有2^n(n<=1000)张牌,从前至后分别为1~2^n,切牌规则,先分成两堆,第一堆依次拿第1张牌、第三张,第五章牌,,第奇数张牌,第二堆拿剩下的,然后有两种放法,可以把第一堆放到第二堆的上面也可以把第二堆放到第一堆的上面,问经过若干次切牌后,是否能够达到第A张牌为X,第B张牌为Y的状态。

解题思路:

感谢frog女神提供的思路。

考虑牌的序号(从0开始)

   0     1     2      3      4      5     6    7 

000 001 010 011 100 101 110 111

计算序号为i的牌,经过一次切牌后的序号。

对于序数为偶数的牌,也就是最低位为0的牌,直接循环右移一位,相当于除以二,这样就凑出了第一堆,对于序数为奇数的牌,最低位为1,循环右移一位后,相当于除二后加了2^(n-1),也就是加了前面偶数堆的长度。对于把奇数堆放到偶数堆前面的方式,只需把最高位抑或1。这样就把位置为i的牌,经过一次切牌后的位置算出来了。对于抑或可以先移位然后所有位一起抑或,但是右移操作最多只有n次,超过n次的话,就重复了,所以可以先右移n次,每右移一次(切牌)有两种方式抑或0或者1,这样就得到了一个长度为n的o、1抑或串,也就是切牌方式。枚举开始点,右移次数k,将X,Y右移K位,由于a^b=c可以推出a^c=b,所以把X抑或A与Y抑或B比较,判断是否相同(相同表示序号为X的牌和序号为Y的牌可以经过相同的切牌方式得到位置A和B)。

任何切牌方式都可以转化到构造的切牌n~2n次范围内。

代码:

import java.math.*;
import java.io.*;
import java.util.*;

public class Main {

	/**
	 * @param args
	 */
	static int n; 
	public static BigInteger righ(BigInteger a)//循环移位
	{
		BigInteger tmp = a.and(BigInteger.valueOf(1)); //取出最低位作为右移后的首位
		
		return a.shiftRight(1).or(tmp.shiftLeft(n-1));//将最低为加到最高位上
		
	}
	
	public static void main(String[] args)
	{
		// TODO Auto-generated method stub
		
		
		Scanner cin = new Scanner(System.in);
		
		int t=cin.nextInt();
		
		BigInteger A,X,B,Y;
		
		for(int ca=1;ca<=t;ca++)
		{
			n=cin.nextInt();
			A=cin.nextBigInteger();
			X=cin.nextBigInteger();
			B=cin.nextBigInteger();
			Y=cin.nextBigInteger();
			
			A=A.add(BigInteger.valueOf(-1)); //从0开始
			X=X.add(BigInteger.valueOf(-1));
			B=B.add(BigInteger.valueOf(-1));
			Y=Y.add(BigInteger.valueOf(-1));
			
			boolean flag=false;
			

			for(int k=1;k<=n;k++)//枚举切牌的起点
			{
				X=righ(X);
				Y=righ(Y);
		
				
				BigInteger a=X.xor(A);
				BigInteger b=Y.xor(B);
				
				if(a.compareTo(b)==0)
				{
					flag=true;
					break;
				}
			}
			if(flag)
				System.out.println("Case "+ca+": Yes");
			else
				System.out.println("Case "+ca+": No");
			
		}

	}

}

抱歉!评论已关闭.