【HDU 1258】Sum It Up（DFS，去重技巧）

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

思路：

• 这道题和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);

}

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;
}