原題目:http://poj.org/problem?id=1014
題目大意:
有分別價值為1,2,3,4,5,6的6種物品,輸入6個數字,表示相應價值的物品的數量,問一下能不能將物品分成兩份,是兩份的總價值相等,其中一個物品不能切開,只能分給其中的某一方,當輸入六個0是(即沒有物品了),這程式結束,總物品的總個數不超過20000
輸出:每個測試案例佔三行:
第一行: Collection #k: k為第幾組測試案例
第二行:是否能分(具體形式見用例)
一直以為自己已經掌握了DFS。今天遇到POJ1014題目,準備深搜一下。沒有用遞迴,記得老師說過對任何遞迴演算法都能用非遞迴演算法實現。然後就認真地開始寫代碼,深知動手越快越容易錯,我還畫了棵樹,大概條理了一下深搜的過程。看起來似乎很正確,沒有任何問題。可是還是那句老話:除非你用代碼將系統實現,並讓它動起來,否則你無法真正瞭解它會有什麼問題。按照自己的思路寫完之後,開始測試,問題就暴露出來,又少考慮某些情況下需要回溯的問題了。
讓我們分析如下兩圖的搜尋情況:
一: 沒有經過深入思考,我採用了第一種深搜方式,如果sum<halfvalue繼續往下探索,如果大於則回溯到上一層,證明上一層選的不合適。認真分析就會發現問題,(1)這個樹中的搜尋路徑很明顯不支援同一個數位若干次選取。(2)僅僅只有六層。這怎麼可以呢!照此說不管怎麼遍曆都是1+2+3+4+5+6.真是汗顏啊怎麼寫出這麼個無厘頭的程式呢!
二: 這種搜尋是合理的。每次探測都可以有六種選擇,也可以想象成從六個盒子裡面拿東西,每次從六個盒子裡選一種,第一種出現的兩種錯誤都可以避免掉。而且每個節點的度都是6,而且特徵一樣。這個非常好,對每個節點的處理都一樣。很明顯可以遞迴。符合DFS的特點。
附上代碼:(借鑒別人的,感覺這段代碼寫的很精緻巧妙啊,讓人慚愧啊)
#include<iostream>using namespace std;int amount[7] = {0};int half_value = 0;int flag = 0;void DFS(int value, int pre){if(value == half_value){flag = 1;return;}if(flag == 1){//不可少的,感受其作用,讓遞迴棧中所有DFS結束return;}int i = 0;for(i = pre; i > 0; i--){if(amount[i]){if(i + value <= half_value){amount[i]--;DFS(i + value, i);if(flag == 1){//不可少的,感受其作用,讓遞迴棧中所有DFS結束return;}}}}}int main(){int testcase = 1;while(true){flag = 0;int totalvalue = 0;int N = 6;int i = 1;while(i <= N){cin >> amount[i];totalvalue += amount[i] * i;i++;}if(!amount[1] && !amount[2] && !amount[3] && !amount[4] && !amount[5] && !amount[6]){break;}printf("Collection #%d:\n", testcase++);if(totalvalue % 2 != 0){cout << "Can't be divided." << endl << endl;continue;}half_value = totalvalue / 2;DFS(0, 6);if(flag){cout << "Can be divided." << endl;} else {cout << "Can't be divided." << endl;}cout << endl;}return 0;}