序列中,除了2个数字只出现一次外,其他数字成对出现。找出落单的2个数。

本题是上一题的续篇,扩展篇。 同时也是jobdu1351题。

想把这一问题先转化为上一个问题,然后解决掉。于是需要将序列分成2类,正好把两个落单的数划分到两个类中去

1,位运算的优先级真是搞不清楚。以后遇上^&|<<>>,这样的运算外都用小括号括上。 if((a[i]&cls) == 0) 这句话 如果不给&加上小括号,就会出错。

2,第一次先把序列异或起来,得到的结果其实就是两个落单的数的异或。分析结果,结果的二进制中为1的位为这两个数不同的,从中随便选一个位作为“分类标准”(我选的是最右边的1)。

3,分类之后,就变为上一问题了(序列中只有一个落单的数)。根据分类标准,将各类中的数字异或起来。就得到了两个落单的数。



#include<cstdio>
#include<iostream>
using namespace std;

int main()
{
  int n;
  unsigned int sum=0;
  scanf("%d",&n);
  if(n%2)
  {
  	printf("impossible!\n");
  	return 0;
  }
  int *a = new int[n];

  for(int i=0;i<n;i++)
    {
      scanf("%d",&a[i]);
      sum ^= a[i];
    }
	//printf("%d\n", sum);

  unsigned int index = 0;
  while(sum)
  {
    if(sum&1 == 1)
      break;
     sum = sum>>1;
     index++;
  }
  unsigned int cls = (1<<index);
  int result1=0,result2=0;
  for(int i=0;i<n;i++)
  {
    if((a[i]&cls) == 0)
      result1 ^= a[i];
    else
      result2 ^= a[i];
  }

  if(result1 < result2)
    printf("%d %d\n", result1, result2);
  else
    printf("%d %d\n", result2, result1);


  delete[] a;
  a=NULL;
  return 1;

}



//1351
#include<cstdio>
#include<iostream>
using namespace std;

int main()
{
  int n;
  while(scanf("%d",&n) != EOF)
  {
    if(n%2)
    {
    	printf("impossible!\n");
    	return 0;
    }
    int *a = new int[n];
    unsigned int sum=0;

    for(int i=0;i<n;i++)
      {
        scanf("%d",&a[i]);
        sum ^= a[i];
      }

    unsigned int index = 0;
    while(sum)
    {
      if(sum&1 == 1)
        break;
       sum = sum>>1;
       index++;
    }
    unsigned int cls = (1<<index);
    int result1=0,result2=0;
    for(int i=0;i<n;i++)
    {
      if((a[i]&cls) == 0)
        result1 ^= a[i];
      else
        result2 ^= a[i];
    }

    if(result1 < result2)
      printf("%d %d\n", result1, result2);
    else
      printf("%d %d\n", result2, result1);


    delete[] a;
    a=NULL;
  }
  return 1;

}




你可能感兴趣的:(序列中,除了2个数字只出现一次外,其他数字成对出现。找出落单的2个数。)