网易笔试编程题-混合颜料

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

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

输入例子:
3
1 7 3

输出例子:
3
做法类似于求用初等行变换来求矩阵的秩。要得到唯一的颜料数字个数,就是求出该矩阵线性无关的基底个数,即秩。将每个数字按照从小到大排列,他们的二进制形式可以看成是一个矩阵,比如1,7,3这三个数字,首先从小到大排列:1,3,7。将他们用二进制表示即为
0001
0011
0111,
官网给的这个例子不好,因为这个矩阵不需要初等行变换就能得到它的秩为3,为此,我们重新造一组输入用例:1, 2, 3, 6, 7, 8, 13。虽然很简单,但能说明问题就好,他们的二进制表示为:
第一行:0001
第二行:0010
第三行:0011
第四行:0110
第五行:0111
第六行:1000
第七行:1011
我们从前往后进行扫描(当然也可以从后往前开始扫描),在这个矩阵中,第二行和第三行最高位可以消除,经过异或,消除最高位,即得到0001,判断该数字在矩阵中是否存在,如果存在,就不插入;第四行和第五行可以消除,经过异或,消除最高位,即得到0001,判断该数字在矩阵中是否存在,如果存在,就不插入;第六行和第七行可以消除,经过异或,消除最高位,得到0011,判断该数字在矩阵中是否存在,若存在,就不插入。
因为我们是要求出基数的个数,所以对于高位相同的两个数字,我们可以删除一个,保留该种类的基数存在一个即可。得到的结果为:
第一行:0001
第二行:0010
第三行:0110
第四行:0011
重新进行排序有:
第一行:0001
第二行:0010
第三行:0011
第四行:0110
继续对相邻两行进行异或操作,得到:
第一行:0001
第二行:0010
第三行:0110
因此,得到该矩阵的秩:3,也就是说,数字1, 2, 3, 6, 7, 8, 13,可有三个数经过异或得到,这三个数可以是:1,2,6,也可以是其他(其他可能是由于选择删除两个高位相同的数字不同而得到不同的数字)。
算法描述:
首先读入元素并存入动态数组vector中,并 对元素从小到大进行排序。开始循环, 定义while循环条件,当动态数组元素个数大于2时,继续循环(因为若要进行颜料混合,最少需要2个,当动态数组的元素少于2个时,就不用进行初等行变换了,即不需要颜色混合了),在循环体外定义了两个下标分别指向动态数组倒数第一个和倒数第二个,判断下标所指向的数字最高位1的位置是否相同,如果相同,则进行异或操作,再判断得到的新值是否在数组中,如果不在,则将新值插入动态数组中,再对其进行排序。如果下标所指向的数字最高位1的位置不同,则计数器加一,将动态数组最后一个元素弹出,两个下标上移(自减)。重复这个过程,直到循环结束,输出动态数组中元素个数(其实是2)+ 计数器值即得到颜料数字基的个数,也就是秩的大小。
程序代码如下:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int theNumOfOne(int num);

int main(void)
{
    int n;
    cin >> n;
    vector<int> nums;
    while (n--)
    {
        int temp;
        cin >> temp;
        nums.push_back(temp);
    }
    sort(nums.begin(), nums.end());
    int rank = 0;
    int theLastOne = nums.size() - 1;
    int uperOfTheLastOne = theLastOne - 1;
    while(nums.size() > 2)
    {
        if (theNumOfOne(nums[theLastOne]) == theNumOfOne(nums[uperOfTheLastOne]))
        {
            int newNum = nums[theLastOne] ^ nums[uperOfTheLastOne];
                        if (find(nums.begin(), nums.end(), newNum) == nums.end())
                        {
                                nums.push_back(newNum);
                                sort(nums.begin(), nums.end());
                                theLastOne ++;
                                uperOfTheLastOne ++;
                        }
        }
        else
        {
            rank ++;
        }
        nums.pop_back();
        theLastOne --;
        uperOfTheLastOne --;
    }
    cout << rank + 2 << endl;
    return 0;
}

int theNumOfOne(int num)
{
    int cnt = 0;
    while(num)
    {
        cnt ++;
        num >>= 1;
    }
    return cnt;
}

若有错误之处,敬请指正。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值