阅读 535

用位运算的思路来解2^n复杂度的题|刷题打卡

掘金团队号上线,助你 Offer 临门! 点击 查看详情

一、题目描述:

image.png

二、思路分析:

队伍分配有几种可能,有2^n次幂可能,这类复杂度一般都想递归回溯,但是没必要。这里出一个代替递归回溯秘籍——位运算法

假设有队伍 abcd,我们用1010表示一队:{a,c} 二队:{b,d}

那么我们只要从 0000 遍历到 1111即可

接下来分析题的具体情况,

首先1010 和0101肯定是冗余的,这样我们可以剪枝一半

接下来0000 和 1111 肯定不能用 再去掉这两种可能

即只需要遍历 0001到1000即可

那么,如果降低寻找最小值的耗时呢

我们将数组排序,用位运算的思路去寻找最小值(这里暂停一下,掘友自己拿笔算算看,算完可以和答案比较下)。

三、AC 代码:

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

int countVec(vector<int> arr, int n){
    int ret =  0;
    for(int i = 0; i <arr.size(); i++){
        if( (1 << i) & n ){
            ret += arr[i];
        }
    }
    return ret;

}


int divide_group(vector<int> arr, int n){
    int len = 1 << n - 2;//排除全0和全1
    int zlen = 1 << n - 1; //由于反码实际上只需要一半
    int sum = 0;
    // 求和
    for(int i = 0; i < n; i++){
        sum += arr[i];
    }

    sort(arr.begin(),arr.end());
    int ret = 0;

    for(int i = 1; i <= zlen; i++ ){

        int s1 = countVec(arr, i);

        int s2 = sum - s1;
        int idx = 0;
        if(s1 - s2 == 0){
                continue;
        }
        int tt = s1 - s2 > 0? 1 : 0; //
        int d = i;
        while(d&1 ^ tt){
                d = d >> 1;
               // cout<< "z:" << i<< endl;
                idx++;
        }

        if(abs(s1 - s2) < 2 * arr[idx]){
            ret++;
        }

    }
    return ret;
}

int main(){
    vector<int> arr;
    arr.push_back(5);
    arr.push_back(4);
    arr.push_back(7);
    arr.push_back(6);
    cout << divide_group(arr,4);
}
复制代码

四、总结:

一般来说,每次回溯穷举,都会让人想要剪枝找到最优解怎么将2n2^n转变成n2n ^ 2

记得在穷途末路的时候试试穷举,别在前路漫漫时放弃前行。

梦在前方,路在脚下。

文章分类
后端
文章标签