算法题:求所有和为N的子集

来源:互联网 发布:centos 6.5配置双ip 编辑:程序博客网 时间:2024/05/07 17:16
    瞎逛时看到这个题目。想来很久没有做算法题了,觉得本题好像挺好玩的,所以抓来练手。
   解释下题目,比如集合{1,2,3,4,5},和为5的子集有{1,4},{2,3},{5}。这个问题有点类似Subset sumproblem,可参考(http://en.wikipedia.org/wiki/Subset_sum_problem)
   不过我没有想到文中那些聪明的做法,还是祭出万金油:回溯(其实应该是回溯+剪枝,但没想出剪枝算法)。这玩意的时间复杂度可是2^N。
static bool possible(int* data, int size, int target)
{
    int min = 0;
    int max = 0;
    for (int i = 0; i < size; ++i)
    {
        if (data[i] >=0)
        {
            max +=data[i];
        }
        else
        {
            min +=data[i];
             
    }
    return (target >= min&& target <=max);
}

class RecursiveSolution0
{
public:

    staticpair<int,int> solute(int* data, intsize, int target, bool print)
    {
        if (data == NULL || size<= 0)
        {
            throwstd::runtime_error("");
             
        int* elements = newint[size];
        int element_cnt = 0;
        int solution_cnt = 0;
        int compare_times = 0;
        if (possible(data, size,target))
        {
           outputSubsetRecursive(data, size, print, 0, target, elements,element_cnt, solution_cnt, compare_times);
        }
        delete[] elements;
        returnstd::make_pair<int,int>(solution_cnt, compare_times);
    }

private:

    static void outputSubsetRecursive(int* data, intsize, bool print, int begin, int target,
        int* elements,int& element_cnt, int&solution_cnt, int& compare_times)
        
        for (int i = begin; i< size; ++ i)
        {
            ++compare_times;
            if(data[i] == target)
           {
               if (print)
               {
                   cout<< "{";
                   for (int j = 0; j< element_cnt; ++ j)
                   {
                       cout<< data[elements[j]]<< ", ";
                   }
                   cout<< data[i]<< "}"<<  endl;
               }
               ++ solution_cnt;              
                               
            if (i + 1!= size)
           {
               elements[element_cnt++] = i;
               outputSubsetRecursive(data, size, print, i+1,target-data[i], elements, element_cnt, solution_cnt,compare_times);
                                          
        }
        if (element_cnt != 0)
        {
            --element_cnt;       
             
    }
};
    如果集合中都是正数,我倒是想出两种剪枝策略,不过需要先对集合从小到大排序。
   策略一)假设我们在{1,2,3}集合中搜索和为3的子集。当搜索进行到到root-1-2的时候,可以直接跳到root-2继续搜索,因为此时元素和等于目标,所以root-1-2的子结点(此例中的root-1-2-3)肯定大于目标(因为都是正数),而排在其后的兄弟结点(此例中的root-1-3)肯定也大于目标(因为集合是升序排列)。 
   策略二)假设我们在{1,2,3,4}集合中搜索和为8的子集。当搜索进行到root-1-3-4的时候,可以跳到root-2继续搜索,因为此时元素和等于目标,所以root-1尚未搜索的子结点(此例中的root-1-4)肯定小于目标(它们都是当前结点的子集)。这个策略是受下面数据的启发,如果集合是{1,2,3,4,5}搜索和为15的集合。当搜索到1-2-3-4-5的时候,就不需要搜索root-2等了。因为他们都是当前集合的子集,当前集合和等于目标,故其他集合的和必然小于目标。这条策略对于子集几乎等于全集的数据很有效。
static bool possible(int* data, int size, int target)
{
    int max = 0;
    int min = 0;
    for (int i = 0; i < size; ++i)
    {
        if (data[i] >0)
        {
            max +=data[i];
            min =std::min<int>(data[i],min);
        }
        else
        {
            returnfalse;
             
    }
    return (target >= min&& target <=max);
}

class RecursiveSolution2
{
public:

    staticstd::pair<int,int> solute(int* data, int size, int target, boolprint)
    {
        if (data == NULL || size<= 0)
        {
            throwstd::runtime_error("");
             
       std::sort(data, data+size);
        int* elements = newint[size];
        int element_cnt = 0;
        int solution_cnt = 0;
        int compare_times = 0;
        if (possible(data, size,target))
        {
           outputSubsetRecursive(data, size, print, 0, target, elements,element_cnt, solution_cnt, compare_times);
        }
        delete[] elements;
        returnstd::make_pair<int,int>(solution_cnt, compare_times);
    }

private:

    static bool outputSubsetRecursive(int* data, intsize, bool print, int begin, int target,
        int* elements,int& element_cnt, int&solution_cnt, int& compare_times)
    {
        bool full_match_flag =false;
        for (int i = begin; i< size; ++ i)
        {
            ++compare_times;
            if(data[i] == target)
           {
               if (print)
               {
                   cout<< "{";
                   for (int j = 0; j< element_cnt; ++ j)
                   {
                       cout<< data[elements[j]]<< ", ";
                   }
                   cout<< data[i]<< "}"<<  endl;
               }
               ++ solution_cnt;
               elements[element_cnt] = i;
               full_match_flag = (i == size - 1);
               break;
           }
            else if(data[i] < target)
                    
               if (i + 1 != size)
               {
                   elements[element_cnt++] =i;
                   if(outputSubsetRecursive(data, size, print, i + 1, target - data[i],elements, element_cnt, solution_cnt, compare_times))
                   {
                       if(elements[element_cnt-1] + 1 == elements[element_cnt])
                      {
                          -- element_cnt;
                          return true;
                                          
                   }
                   --element_cnt;                     
                           
           }
            else if(data[i] > target)
           {
               break;
           }
             
        return full_match_flag;
    }
};

0 0