1. 程式人生 > 實用技巧 >子集和問題(回溯法)

子集和問題(回溯法)

一、問題簡介

描述

子集和問題的一個例項為〈S,t〉。其中,S={ x1 , x2 ,…,xn }是一個正整數的集合,c是一個正整數。子集和問題判定是否存在S的一個子集S1,使得:SUM(S1) = c

試設計一個解子集和問題的回溯法。

對於給定的正整數的集合S={ x1 , x2 ,…,xn }和正整數c,計算S 的一個子集S1,使得:SUM(S1)=c。

Input

輸入資料的第1 行有2 個正整數n 和c(n≤10000,c≤10000000),n 表示S 的大小,c是子集和的目標值。接下來的1 行中,有n個正整數,表示集合S中的元素。

Output

將子集和問題的解輸出。當問題無解時,輸出“No Solution!”。

Sample Input

5 10
2 2 6 5 4

Sample Output

2 2 6

二、問題分析

排列樹回溯

時間複雜度為 O(n!) 可通過加限制條件達到剪枝效果

三、程式碼

#include <iostream>
 
using namespace std;
 
int n=0,c=0;
int d[10050];
int t[10050];
int l=0;
int flag = 0;
int mysum = 0;
 
void backtrack(int,int,int);
 
int main(){
    //輸入
   cin>>n>>c;
    for (int i = 0; i < n; ++i) {
        cin>>d[i];
    }
 
    //回溯呼叫
    for (int i = 1; i <= n; ++i) {
        backtrack(i,0,n-1);
        if(flag == 1)
            break;
    }
 
    //輸出
    if (flag == 0){
        cout<<"No Solution!"<<endl;
    }
    else{
        for (int i = 0; i < l-1; ++i) {
            cout<<t[i]<<' ';
        }
        cout<<t[l-1]<<endl;
    }
}
void backtrack(int x,int s1,int s2){
 
    if (x == 0){
        if(mysum == c)
            flag = 1;
        //cout<<mysum<<endl;
        return;
    }
 
    for (int i = s1; i <= s2; ++i) {
        mysum += d[i];
        t[l++] = d[i];
        if (mysum<=c)   //剪掉不可能的分支
            backtrack(x-1,i+1,s2);
        if (flag == 1)  //找到結果,退出
            return;
        l--;
        mysum -= d[i];
    }
 
}