1. 程式人生 > >Sum It Up HDU - 1258(不回溯的DFS+去重)

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