《为什么会变成这样呢》
Description
Input
Output
Sample Input
Sample Output
首先,在看完题后很容易理解就是在一堆数字中找出只出现一次的两个数字。(而其他的数字都出现两次。)刚开始我看完题后就想到了用位运算的异或来解。但是,在比赛中转念一想“异或不是只能得出一个数字吗?”于是就放弃了使用异或的做法,转而使用排序、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);
//最后输出时只需判断大小即可。
}
}
虽然说现在看看倒是比较水,但是作为我个人而言也写一下稍微纪念一下了。