Sum It Up HDU - 1258(不回溯的DFS+去重)

本题并不是一般的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;
}

猜你喜欢

转载自blog.csdn.net/mch2869253130/article/details/86292019