掘金团队号上线,助你 Offer 临门! 点击 查看详情
一、题目描述:
二、思路分析:
队伍分配有几种可能,有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);
}
四、总结:
一般来说,每次回溯穷举,都会让人想要剪枝找到最优解怎么将转变成。
记得在穷途末路的时候试试穷举,别在前路漫漫时放弃前行。
梦在前方,路在脚下。