使用 DFS 枚举子集

Enumerate subsets with DFS

本文关键字:子集 枚举 DFS 使用      更新时间:2023-10-16

我想枚举给定数组的所有可能子集,总共应该是 2^N。在这里,我使用 sum 来表示所有子集,因为在这个例子中,总和是不同的。我使用一个名为 take 的辅助数组来标记当前项目是否被采用并执行 DFS。这是代码:

void dfs(vector<int>& res, vector<int>& nums, vector<bool>& taken, int sum, int pos) {
    for (int i = pos; i < nums.size(); ++i) {
        if (!taken[i]) {
            taken[i] = true;
            res.push_back(nums[i] + sum);
            dfs(res, nums, taken, sum + nums[i], pos + 1);
            taken[i] = false;
        }
    }
    return;
}
int main() {
    vector<int> test = {1, 10, 100, 1000};
    vector<bool> t(4, false);
    vector<int> res;
    dfs(res, test, t, 0, 0);
    return 0;
}

但是此代码不会返回 2^n 结果。

我已经修复了你的代码

void dfs(vector<int>& res, vector<int>& nums,  int sum, int pos) {
    for (int i = pos; i < nums.size(); ++i) {
        res.push_back(sum + nums[i]);
        dfs(res, nums,  sum + nums[i], i + 1); // i + 1 instead of pos + 1
    }
    return;
}
int main() {
    vector<int> test = {1, 2, 3};
    vector<int> res;
    dfs(res,test,0, 0);
    cout << res.size() << endl;
    copy(res.begin(), res.end(), ostream_iterator<int>(cout , " "));
    cout <<endl;
    return 0;
} 

而且由于您不会向后访问元素(即已经添加的索引),因此您不必维护一个数组来检查是否访问过。

void enumerate_all_subsets(vector <int> &res, const vector <int> &nums) {
    if(nums.empty()) return;
    for(auto i = 0ull; i < (1 << nums.size()); ++ i) {
        int sum = 0;
        for(auto k = 0; k < nums.size(); ++ k)
            if((i >> k) & 1)
                sum += nums[k];
        res.push_back(sum);
    }
}

这将迭代大小最大为 63 的向量(如果您的无符号长整型为 64 位)。任何更大的向量可能都应该重新考虑,因为这需要一段时间,因为这O(2^n),正如你所指出的。

基本上,unsigned long long i的每个位代表一个nums的位置,以及是否应该将其添加到总和中。