网易内推编程题:异或运算求混合颜料的最小种类

矩阵的秩定义:是其行向量或列向量的极大无关组中包含向量的个数。

类似,颜料的最小种类等价于求由颜料组成的矩阵,求矩阵的的基的个数。

矩阵的秩求法:用初等行变换化成梯矩阵, 梯矩阵中非零行数就是矩阵的秩.

类似,基的求法是异或运算,而不是相加减。

基是类似与1,10,100,1000...这样的数。如果颜色矩阵有1,10,100,1000四个数,则可以异或出0001~1111共15种颜色。

/************************************************************************/
/* 你就是一个画家!你现在想绘制一幅画,但是你现在没有足够颜色的颜料。为了让问题简单,我们用正整数表示不同颜色的颜料。你知道这幅画需要的n种颜色的颜料,你现在可以去商店购买一些颜料,但是商店不能保证能供应所有颜色的颜料,所以你需要自己混合一些颜料。混合两种不一样的颜色A和颜色B颜料可以产生(A XOR B)这种颜色的颜料(新产生的颜料也可以用作继续混合产生新的颜色,XOR表示异或操作)。本着勤俭节约的精神,你想购买更少的颜料就满足要求,所以兼职程序员的你需要编程来计算出最少需要购买几种颜色的颜料? 
输入描述:

第一行为绘制这幅画需要的颜色种数n (1 ≤ n ≤ 50)
第二行为n个数xi(1 ≤ xi ≤ 1,000,000,000),表示需要的各种颜料.


输出描述:

输出最少需要在商店购买的颜料颜色种数,注意可能购买的颜色不一定会使用在画中,只是为了产生新的颜色。
示例1
输入

3
1 7 3
输出

3                                                                     */
/************************************************************************/

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

int GetBitLength(int k){
	int j = 0;
	while(k > 0){
		k >>= 1;
		++j;
	}
	return j;
}

int main(){
	int i, j, n;
	cin>>n;
	vector<int> v(n);
	for(i = 0; i < n; ++i){
		cin>>v[i];
	}
	sort(v.begin(), v.end());
	int res = 0;
	while(v.size() > 1){
		vector<int> xor_num;
		j = v.size() - 1;
		while(j > 0 && GetBitLength(v[j]) == GetBitLength(v[j - 1])){//倒数两个数的最高位是否相同
			int xor_val = v[j] ^ v[j - 1];//最高位相同,则寻找能代表这么高位的基
			if(xor_val != 0 && find(v.begin(), v.end(), xor_val) == v.end()){//异或结果在颜色矩阵里存在,则可以去掉最大数,不受影响
				xor_num.push_back(xor_val);//异或结果不存在,则需保留异或结果,去掉最大数后,这个结果与次大数异或即可得到最大数
			}
			v.pop_back();//不管异或结果在颜色矩阵里是否存在,都可以放心去掉最大数
			--j;
		}
		++res;//GetBitLength(v[j]) != GetBitLength(v[j - 1])时,最高位不同,则v[j]这颜料必须得买,否则这个最高位没法异或出来
		v.pop_back();//v[j]最大数必须得买,res也计数了,可以放心得去掉v[j]
		if(xor_num.size() > 0){
			v.insert(v.end(), xor_num.begin(), xor_num.end());//既然把最大数给去掉了,为了异或得到最大数,颜色矩阵需加进来之前算出的异或结果
			sort(v.begin(), v.end());
		}
	}
	cout<<res + v.size()<<endl;
	return 0;
}


posted @ 2017-08-14 14:39  白水冰泉  阅读(254)  评论(0编辑  收藏  举报