本题并不是一般的DFS,一般的DFS是需要带回溯的,比如是下面的形式
mark[i] = true;
DFS();
mark[i] = false; //用于回溯
//具体可见Prime Ring Problem HDU - 1016
本题要考虑清楚以下几个问题
如何保证输出数据按照非递增顺序输出
仔细观察本题的条件就可知道,题目要求输出的时候按照非递增的顺序输出,但由于输入也是按照非递增的顺序输入的,在DFS的for循环中按照输入list中的非递增顺序进行遍历,在ans数组中存储答案,那么ans中的数必定也是按照非递增的顺序存储的。
如何去重
去重的代码在下面有注释,具体思路是如果当前正在遍历list[i],若
list[i+1] == list[i],则跳过list[i+1](跳过的意思是不把list[i+1]作为参数,传进DFS()函数中),即可去重。
为什么本题DFS不需要回溯
以输入数据4 6 4 3 2 2 1 1为例,当从3开始遍历时,得到输出数据 3 1,若添加回溯,从1开始遍历,会得到 1 3,这样与前面的 3 1 重复了,故本题不能回溯。而且由于本题的输入数据是按照非递增的顺序输入的,DFS()中的for循环也是按照非递增顺序遍历的,即若list[i] > list[j],则必有i < j,如果此时list[i] + list[j] == t,那么在整个解空间中,由list[i],list[j]组成的解在遍历较大的list[i]时就已经得到了,故不需要在遍历较小的list[j]的时候回溯找list[i]。
//AC代码
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int list[12], ans[12];
int t, n;
bool flag; //用于标识是否输出NONE
void print(int length)
{
for(int i = 0; i < length; i++)
if(i == 0)
printf("%d", ans[i]);
else
printf("+%d", ans[i]);
printf("\n");
}
//tmp表示当前由ans[0]-ans[y-1]的和,
//x表示本次dfs从list[x]开始遍历
//y表示ans已经存入的元素个数
void DFS(int tmp, int x, int y)
{
if(tmp == t)
{
flag = true; //至少打印了一次,则不输出NONE
print(y);
return;
}
for(int i = x; i < n; i++)
{
if(list[i] + tmp > t)
continue;
ans[y] = list[i];
DFS(tmp+list[i], i+1, y+1);
while(i+1 < n && list[i] == list[i+1]) //去重
i++;
}
}
int main()
{
while(~scanf("%d %d", &t, &n) && t)
{
for(int i = 0; i < n; i++)
scanf("%d", &list[i]);
flag = false;
printf("Sums of %d:\n", t);
DFS(0, 0, 0);
if(!flag)
printf("NONE\n");
}
return 0;
}