【回溯法】重複元素集合 求子集

問題:給定一個可能具有重複數字的列表,返回其所有可能的子集。

樣例 1:
輸入:[0]
輸出:
[
  [],
  [0]
]
樣例 2:
輸入:[1,2,2]
輸出:
[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]

思路:

  1. 使用回溯法的遞歸框架;
  2. 解空間爲子集樹;
  3. 對於集合中出現了重複的數字,需要設計剪枝函數check(i)。規定重複元素的選取狀態只能是前半部分1,後半部分0,從而排除重複項。例如【1,3,3,3】,對於重複元素3的選取,1 0 0結果是【3】,0 1 0結果也是【3】,0 0 1結果還是【3】。那麼只保留第一種情況,0後不能再取1就好了,遇到其餘情況便不再繼續下遞歸。
  4. 除此之外,還有另一位老哥的辦法,在去重複元素求子集的基礎上再一步一步添加重複元素,記錄一下。
    跳轉到該鏈接

參考代碼:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100;
int v[maxn] = {0},x[maxn] = {0};
int backtrack(int i,int n);
int check(int i);
int main(){
	printf("求n個元素子集,請輸入n的值\n"); 
	int n;
	scanf("%d",&n); 
	printf("請輸入集合元素\n"); 
	for(int i = 1 ; i <= n ; i++ )
		scanf("%d",&v[i]);
	backtrack(1,n);
	return 0;
} 
int backtrack(int i,int n){
	if(i > n ){
		cout<<"{ ";
		for(int tempi = 1;tempi <= n; tempi++){
			if(x[tempi] == 1){
				printf("%d",v[tempi]);
			}
		}
		cout<<" }"<<endl;
	}else{
		for(int j = 0 ;  j <= 1 ; j++){
			x[i] = j;
			if(check(i))
				continue; 
			backtrack(i+1,n);
		}
	}
} 
int check(int i){
	if(i != 1 && v[i] == v[i-1] && x[i-1] == 0 && x[i] == 1)
		return 1;
	else
		return 0; 
}

LintCode參考代碼:
class Solution {
public:
    /**
     * @param nums: A set of numbers.
     * @return: A list of lists. All valid subsets.
     */
    vector<vector<int>> v;
    vector<int> tempv;
    int x[1000]={0};
    vector<vector<int>> subsetsWithDup(vector<int> &nums) {
        int n = nums.size();
        sort(nums.begin(),nums.end());
        backtrack(0,n,nums);
        return v;
    }
    int backtrack(int i,int n,vector<int> nums){
	if(i >= n ){
	    tempv.clear();
		for(int tempi = 0;tempi < n; tempi++){
			if(x[tempi] == 1)
				tempv.push_back(nums[tempi]);
		}
		v.push_back(tempv);
	}else{
		for(int j = 0 ;  j <= 1 ; j++){
			x[i] = j;
			if( i!=0 && nums[i] == nums[i-1] && x[i-1] == 0 && x[i] == 1)
				continue; 
			backtrack(i+1,n,nums);
		}
	}
}
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章