子集和问题

SDUT OJ 子集和问题链接

Problem 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个正

此问题是一个典型的搜索问题。需要依次尝试每一种可能。
问题的难点在于怎样尝试每一种可能。
回看问题,该问题有2^n中子集,用正常的for循环不可能找到问题的解,
所以只可能用递归实现,联系搜索算法,所以我们就想到了DFS。
既然是搜索,首先我们要画出来他的搜索树,根据搜索树方可得到代码
因为2 2 6 5 4 五个元素都需要访问,并且第一个元素不一定是最优解
所以我们在搜索的时候需要补充一个根节点0!
若不补充根节点0,则每次搜索都会包含第一个数字,导致结果可能错误!
下面是我画的搜索树,因为该问题不需要每次都把所有的子集用到。
举一个例子,本题是要搜索10,假如是5 8 9 6 4,当搜索到8的时候不满足条件
我们就可以直接跳到9,跳到9的前提是前面的数字不满足条件或者前面的数字已经用了
所以,我们就不需要再去尝试前面的数字,只需要尝试我们没有尝试过的数字。
所以该搜索树会的每一颗子树可能不等高。

子集和问题_第1张图片

#include
using namespace std;
#define N 10010
int n, s;
//数组a[N]代表输入的子集,用sum判断是否与s相等,
//k用来计数已经存入数组中元素的个数
int a[N] = {0}, sum = 0, k = 0;
//用数组v来存放符合条件的子集
int v[N] = {0};
//flag判断是否已经找到满足条件的子集
bool flag = false;
void DFS(int x)
{
    //这两个语句必须放在这个位置,根据搜索树可以知道
    //要补加一个根节点0,若在for里面,根节点不起作用!
    sum += a[x];
    v[k++] = a[x];
    //剪枝操作
    if(sum > s)
        return;
    if(sum == s)
    {
        flag = true;
        return;
    }
    for(int i = x + 1; i <= n; i++)
    {
 
        DFS(i);
        //本次算法的核心!!!
        //必须是在这里进行判断,若没找到,则回溯
        //若找到了,则返回。
        if(!flag)
        {
            k--;
            sum -= a[i];
        }
        //如果找到了,立马返回,否则可能会输出冗杂内容
        else
            return ;
    }
}
int main()
{
    cin >> n >> s;
    int temp = 0;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        temp += a[i];
    }
    //这里进行判断,防止超时。
    if(temp < s)
    {
        cout << "No Solution!" << endl;
        return 0;
    }
    //从根节点0开始访问!
    DFS(0);
    if(flag)
    {
         for(int i = 1; i < k; i++)
         {
             printf("%d%c", v[i], i==k-1?'\n':' ');
         }
    }
    else
        cout <<"No Solution!"<<endl;
    return 0;
}

你可能感兴趣的:(搜索,搜索,算法,C++,回溯,DFS)