子集和问题

想到该问题是因为去哪儿网的笔试编程题:包机商卖机票,通过查资料发现该题的实质就是子集和问题。所谓的子集和问题就是给定一个包含n个正整数的集合,并给定m,从集合中找出几个元素(也就是一个子集)使它们相加的和等于m。

方法一:穷竭搜索。就是考虑该集合中的所有子集,判断是否相等。子集个数为:2^n。

方法二:回溯算法。在穷竭算法中,不管子集值不值得比较,都会计较。回溯算法会对子集做一个选择。

其思想是:从第一个元素开始,如果此时当前元素不在自己中的话,将这个元素加到子集中(用一个数组标记是否在子集中,1表示在,0表示不在),将sum加上这个值。然后判断,如果sum==目标值,返回结果;如果sum>目标值,舍弃当前元素,修改标记数组,并将sum减去钙元素。只要还有元素没有判断就继续选择,直到第n个元素。如果第n个元素判断完还没有找到符合要求的子集,就回溯到上一次选择的点,将其从集合里删除并从它后一个点开始继续重复前面的操作。如果回溯的时候回溯到第一个元素之前,表示所有元素加入子集都不够,或者所有情况都找过了不符合要求,返回无解。
代码如下:

#include
using namespace std;

#define MAX 10000
bool mark[MAX] = {0};//标记数组

bool func(int n,int m,int* arr)
{
    int sum = 0;
    int i = 0;

    while(i>=0)
    {
        if(!mark[i])
        {
            sum += arr[i];
            mark[i] = true;
            if(sum == m)
                return true;
            if(sum > m)
            {
                mark[i] = false;
                sum -= arr[i];
            }
            if(i>=n-1 && sumreturn false;
            i++;
        }
        if(i >= n)
        {
            while(mark[i-1])
            {
                i--;
                mark[i] = false;
                sum -= arr[i];
                if(i < 1)
                    return false;
            }
            while(!mark[i-1])
            {
                i--;
                if(i < 1)
                    return false;
            }
            sum -= arr[i-1];
            mark[i-1] = false;
        }
    }
    return false;
}

int main()
{
    int n = 0;//n个元素
    int m = 0;//所求的和
    cin>>n>>m;
    int arr[5] = {0};
    int i = 0;
    while(iint tmp = 0;
        cin>>tmp;
        arr[i++] = tmp;
    }

    if(func(n,m,arr))
    {
        for(int i=0;iif(mark[i])
                cout<' ';
        }
        cout<else
        cout<<"no anser"<return 0;
}

你可能感兴趣的:(笔试题,包机商卖机票,去哪儿网,子集和)