【HDU 1258】Sum It Up(DFS,去重技巧)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Chen_yuazzy/article/details/76408946

Sum It Up


Description

Given a specified total t and a list of n integers, find all distinct sums using numbers from the list that add up to t. For example, if t=4, n=6, and the list is 4,3,2,2,1,14,3,2,2,1,1, then there are four different sums that equal 4: 4,3+1,2+2, and 2+1+1.(A number can be used within a sum as many times as it appears in the list, and a single number counts as a sum.) Your job is to solve this problem in general.


Input
The input will contain one or more test cases, one per line. Each test case contains t, the total, followed by n, the number of integers in the list, followed by n integers x1,…,xn. If n=0 it signals the end of the input; otherwise, t will be a positive integer less than 1000, n will be an integer between 1 and 12(inclusive), and x1,…,xn will be positive integers less than 100. All numbers will be separated by exactly one space. The numbers in each list appear in nonincreasing order, and there may be repetitions.

Output
For each test case, first output a line containing ‘Sums of’, the total, and a colon. Then output each sum, one per line; if there are no sums, output the line ‘NONE’. The numbers within each sum must appear in nonincreasing order. A number may be repeated in the sum as many times as it was repeated in the original list. The sums themselves must be sorted in decreasing order based on the numbers appearing in the sum. In other words, the sums must be sorted by their first number; sums with the same first number must be sorted by their second number; sums with the same first two numbers must be sorted by their third number; and so on. Within each test case, all sums must be distince; the same sum connot appear twice.


Sample Input
4 6 4 3 2 2 1 1
5 3 2 1 1
400 12 50 50 50 50 50 50 25 25 25 25 25 25
0 0

Sample Output
Sums of 4:
4
3+1
2+2
2+1+1
Sums of 5:
NONE
Sums of 400:
50+50+50+50+50+50+25+25+25+25
50+50+50+50+50+25+25+25+25+25+25


题意

给你一个待求数N,接下来是个数nn个数组成的数组,求由此数组中任意个数的数组成非递增的排列,其等于待求数N。记得输出“Sums of N:”,没有排列输出NONE


思路:

  • 这道题和HDU-1342 Lotto很相似,都在DFS中套用了两层递归。1342那题规定了输出元素个数,只需求出所有排列。因此搜索出口很方便找到。而此题是要求寻找符合“和等于所给值”的所有排列,甚至可能找不到。
  • 上代码,大致讲解一下:
void DFS(int num,int pos,int sum)
{
    if(sum==N)
    {
        judge=true;   //存在一个以上解 
        for(int i=1;i<num;i++){  //按格式输出 
            if(i==1) cout<<br[i];
            else cout<<"+"<<br[i];
        }
        cout<<endl;return;
    }
    if(sum>N) return ; //若大于,如后一个数特别大之类情况 
    if(pos>n) return ;

    br[num]=ar[pos]; 
    DFS(num+1,pos+1,sum+br[num]);
    while(pos+1<=n&&ar[pos]==ar[pos+1]) pos++;//重要 
    DFS(num,pos+1,sum);

}

当sum==N即所得排列的和为所求时,judge改变(方便之后判断是否有解),并输出此排列。
当sum>N即所得排列的和大于给定值时,终止函数
当pos>n即寻找位置超出数组范围时,终止函数
接下来将ar数组pos位置赋值给br数组num位置

那么,难点来了,下面的代码:

DFS(num+1,pos+1,sum+br[num]);
while(pos+1<=n&&ar[pos]==ar[pos+1]) pos++;//重要 
DFS(num,pos+1,sum);
  • 与Lotto类似:现在选中的是数组ar的pos位置,如果选他的话,就看b数组下一位置(num+1)选谁(pos+1),同时sum也加和,如果不选他(num不加)的话,(先去重),对于这一位置(b数组num位置),将会选a数组接下来的哪一个数字。既然没选ar[pos],当然sum也就没有加和的必要啦。
    哎,去重是神马情况?(重点、难点)

    • 在数组范围内,若前后俩个数相等,则跳过这个数,pos++即指向原数组ar的位置量一直后移直到找到一个新的数(与之前的不等),再进行第二次DFS。
      那这样不会有什么问题吗?
      比如样例中:

      400 12 50 50 50 50 50 50 25 25 25 25 25 25

    • 第一个50赋值给br后,从第一个50直接跳到25。嗯?那岂不输出不了正确结果了?其实不然。DFS是层层递归,当进入后一层递归时,想一想,会发生什么?他也会和第一次一样,第二个50赋值给br后,从第二个50直接跳到25,之后每一层都这样,每一次都选择了一个50。因此并不会出现漏项的情况。学过数据结构的同学都知道,递归是栈,这可以理解成从后面(不定是最后一个50)的某个50向前一直到第一个50。


代码示例:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int N,n;
int ar[15];
int br[15];

bool judge=false;

void DFS(int num,int pos,int sum)
{
    if(sum==N)
    {
        judge=true;   //存在一个以上解 
        for(int i=1;i<num;i++){  //按格式输出 
            if(i==1) cout<<br[i];
            else cout<<"+"<<br[i];
        }
        cout<<endl;return;
    }
    if(sum>N) return ; //若大于,如后一个数特别大之类情况 
    if(pos>n) return ;

    br[num]=ar[pos]; 
    DFS(num+1,pos+1,sum+br[num]);
    while(pos+1<=n&&ar[pos]==ar[pos+1]) pos++;//重要 
    DFS(num,pos+1,sum);

}

int main(){
    while(cin>>N>>n&&N&&n)
    {
        for(int i=1;i<=n;i++) cin>>ar[i];
        judge=false;
        cout<<"Sums of "<<N<<":"<<endl;

        DFS(1,1,0);

        if(!judge)  cout<<"NONE"<<endl;
    }


    return 0;
} 
展开阅读全文

没有更多推荐了,返回首页