A - 子集和問題


Description

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


試設計一個解子集和問題的回溯法。
對於給定的正整數的集合S={ x1 , x2 ,…,xn }和正整數c,計算S 的一個子集S1,使得:

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

Output

2 2 6

題解:

遞歸回溯尋找子集,分為該數字在這個子集中和不在這個子集中兩種情況。遞歸也是通過這兩種情況進行的,但是注意“剪枝”,不然會超時。
而且這道題數據量十分大,就算剪枝也會超時。所以要多加一次判定。
另外這個題目可能存在多個解,然而數據偏弱,所以只要從第一個數據進行遞歸就能得到正解。其他解不用管。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define maxn 11234

using namespace std;

/**
*n集合大小
*k目標值
*s集合
*sum當前以累加值
*num當前子集的大小。
*flag標記是否已經找到子集。
*f記錄當前子集。
*/
int s[maxn], k, n, sum, num, flag, f[maxn];

void slove(int i){
    //如果已經找到合適的子集,結束遞歸。
    if(flag){
        return;
    }
    int j;
    //累加。
    sum += s[i];
    //當前數據進入子集。
    f[num++] = s[i];
    //大於目標值,,剪枝,結束遞歸。
    if(sum > k)
        return;
    //已經找到合適的子集。
    else if(sum == k){
        flag = 1;
        return;
    }
    //繼續還沒達到目標值,繼續累加。
    for(j=i+1; j<n; j++){
        slove(j);
        if(!flag){
            //說明當前數據不適合進入子集,回溯。
            sum -= s[j];
            num--;
        }
        else{
            return;
        }
    }
}

int main(){
    int i;
    scanf("%d%d",&n,&k);
    sum = 0;
    //計算子集所有數據相加能否達到目標值,沒有這一步驟會超時。
    for(i=0; i<n; i++){
        scanf("%d",&s[i]);
        sum += s[i];
    }
    if(sum < k){
        printf("No Solution!\n");
        return 0;
    }
    sum = num = flag = 0;
    //開始進入遞歸。
    for(i=0; i<n; i++){
        slove(i);
        if(!flag){
            sum -= s[i];
            num--;
        }
        else{
            break;
        }
    }
    if(flag){
        for(i=0; i<num; i++){
            printf("%d%c", f[i], i==num-1 ? '\n' : ' ');
        }
    }
    else{
        printf("No Solution!\n");
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM