位运算异或的应用 例题来自广东工业大学OJ

《为什么会变成这样呢》

Description

“第一次有了喜欢的人,还得到了一生的挚友,两份喜悦互相重叠,这双重的喜悦又带来了更多更多的喜悦,本应已经得到了梦幻一般的幸福时光,然而,为什么,会变成这样呢?”双重的喜悦感却无法带来更多的幸福,现在,雪菜在很多喜悦感之中只想要得到两份不重叠的喜悦感(其他的喜悦感都是重叠的),你能帮她找出这两份不同的喜悦感是多少吗?

Input

第一行一个整数T,代表数据的组数(1<=T<=10),接下来T组数据,每组数据的第一行是一个整数n(1<=n<=1000000),第二行是n个整数ai(0 <= ai <= 1000000000)代表喜悦感,每两个整数之间有一个空格,题目保证有且仅有两个不同的整数出现一次,其他的整数都是出现两次。

Output

对于每组数据,输出两个整数,分别代表两份不同的喜悦感,中间有一个空格,并且喜悦感较小的先输出。

Sample Input

2
6
2 2 1 1 3 4
4
1 1 3 4

Sample Output

3 4
3 4

           首先,在看完题后很容易理解就是在一堆数字中找出只出现一次的两个数字。(而其他的数字都出现两次。)刚开始我看完题后就想到了用位运算的异或来解。但是,在比赛中转念一想“异或不是只能得出一个数字吗?”于是就放弃了使用异或的做法,转而使用排序、map、set等各种容器。(我当时虽然是超时了,但也可能是我当时把代码写撮了吧!)结果,我也就迎来了各种超时。最后正当我,回头琢磨琢磨用异或怎么解时已经来不及了。(最后事实证明即使在给我一个小时我也解不出来。)

          最后,我求助一位大佬才成功把这个题补上了........(最后还是用位运算过的。)


         因为要完成这个题是建立在位运算的基础上的。所以,在说思路之前先来简单的提一下几个位运算。(所有的位运算都是在二进制而且是同一二进制位的基础上进行的。这位大佬的博客上对位运算的应用也有很全的总结:http://blog.csdn.net/superdullwolf/article/details/4649080

1:按位与“&”:双目运算,在同一二进制位上如果有都为‘1’,结果对应的二进制位也为‘1’,否则为‘0’。

2:按位或“|”:双目运算,在同一二进制位上如果有一个为‘1’,结果对应的二进制位就为‘1’,否则为‘0’。

3:按位异或“^”:双目运算,在同一二进制位上如果相同得“0”,不同得“1”。

4:按位非“~”:单目运算,把一个数的二进制位“0”变“1”,“1”变“0”。

5:左/右  移运算符"<<"/">>":把一个数的二进制位左/右移任意位,左移是低位补“0”,高位丢弃。右移时低位丢弃高位补“0”,如果有符号是需要注意,符号位也会一起移动。


      题目思路:假设这两个不同的数是A和B,因为题中已经说明了除了这两个数以外别的数都出现了两次。所以,先把开始就必须要先把所有输入的数字都进行一次异或。这样得到的值就是A^B。接下来我们的要做的就是如何把这些数字分成两部分,两部分中各含一个A或者B。因为A和B是不同的,所以A^B的值不会是0,(也就是说在它 的所有二进制位中必然会有一个1,当然也可能不只一个。)然后在这些二进制位的1中需取任意一个二进制位。在这一位上A和B与此对应的二进制位分别是一个是“1”一个是“0”。(根据异或运算计算的过程。)根据这一点我们可以把那些数字分成两部分,一部分是在该二进制位上位“1”的部分,另一部分就是在该二进制位上位“0”的部分,然后在进行一次异或就行了。完成这些的详细的细节在代码中会进行说明。


#include<stdio.h>
#define MAX 1000005
int num[MAX];
int main()
{
	int T;
	scanf("%d",&T);
	while(T--){
		int n;
		scanf("%d",&n);
		int sum=0;   
//在sum中存储的就是把所有输入的值进行异或后的数,也就是最终也就是A^B。 
		for(int j=0;j<n;j++){
			scanf("%d",&num[j]);
			sum^=num[j];
		}
		//cout<<sum<<endl;//此处可以查看A^B的值。 
		int c=0;
		//通过这个用来存sum最低位的"1"。下面的循环就是找"1"的过程。 
		while(!((sum>>c)&1)){
			c++;
		}
		int A=sum;
		for(int j=0;j<n;j++){
			if((num[j]>>c)&1){
				//cout<<num[j]<<endl;
		//在此处可以查看在所有在第c位二进制位数为"1"的数的部分。 
				A^=num[j];
			}
		}
		//cout<<A<<endl; 
		//在找到此时可以查看A的值。 
		int B=sum^A;
		//在确定A是正确后再进行一次异或便可得到B的值。 
		if(A<B){
			printf("%d %d\n",A,B);
		}else
			printf("%d %d\n",B,A);
		//最后输出时只需判断大小即可。 
	}
}

          虽然说现在看看倒是比较水,但是作为我个人而言也写一下稍微纪念一下了。





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值