PAT-ADVANCED1053——Path of Equal Weight
我的PAT-ADVANCED程式碼倉:https://github.com/617076674/PAT-ADVANCED
原題連結:https://pintia.cn/problem-sets/994805342720868352/problems/994805424153280512
題目描述:
題目翻譯:
1053 相等權重的路徑
給定一棵非空樹,其根節點為R,每個節點Ti都有一個相應的權重Wi。從R節點到葉子節點L的路徑權重被定義為該路徑上所有節點的Wi總和。
任給一棵權重樹,你需要找出路徑權重和給定數字相等的所有路徑。舉個例子,讓我們考慮下圖中的這棵樹:對每一個節點,上方的數字是一個兩位數的節點ID,而下方的數字是這個節點對應的權重。假設給定數字24,圖中有4條不同的路徑,其權重均是24:{10 5 2 7}, {10 4 10}, {10 3 3 6 2}和{10 3 3 6 2},在圖中以紅線標識出。
輸入格式:
每個輸入檔案包含一個測試用例。在每個測試用例中,第一行有數0 < N <= 100,代表樹中的節點總數,M(< N),代表非葉子節點數,以及0 < S < 2 ^ 30,代表給定的權重總值。接下來的一行包含N個正整數,分別表示Ti節點的權重值Wi(< 1000)。接下來的M行,每行都是以下格式:
ID K ID[1] ID[2] ... ID[K]
其中ID是一個兩位數的數字,代表一個非葉子節點的編號,K是它的子節點總數,緊跟著的是一串子節點ID值。為簡單起見,根結點ID值為00。
輸出格式:
對每個測試用例,以非遞增的順序列印所有權重為S的路徑。每一行輸出從R到L的節點權重值。一行中所有的數字由一個空格分隔,行末不得有多餘空格。
提示:如果對於i = 1, ⋯, k,其中1 <= k < min{n, m},均有Ai = Bi,且Ak+1 > Bk+1,則序列{A1,A2,⋯,An}被定義為比序列{B1,B2,⋯,Bm}要大。
輸入樣例:
20 9 24
10 2 4 3 5 10 2 18 9 7 2 2 1 3 12 1 8 6 2 2
00 4 01 02 03 04
02 1 05
04 2 06 07
03 3 11 12 13
06 1 09
07 2 08 10
16 1 15
13 3 14 16 17
17 2 18 19
輸出樣例:
10 5 2 7 10 4 10 10 3 3 6 2 10 3 3 6 2
知識點:樹的深度優先遍歷(回溯)
思路:樹的深度優先遍歷(回溯)
幾個注意點:
(1)這是一棵普通性質的數,每個節點的孩子數都是不固定的,因此我們需要用一個結構體node存放節點的資料域和指標域。對於資料域,由兩部分組成,分別是節點編號num和節點權值weight。對於指標域,我們使用靜態寫法,用一個vector<int>型的變數child來存放所有孩子節點的編號。
(2)最後的輸出需要按權值從大到小排序,因此我們在讀入時就事先對每個節點的子節點vector進行排序(即對vector中的節點按權值從大到小排序),這樣在遍歷時就會優先遍歷到權值大的子節點。
(3)令vector<int>型的便來來儲存路徑。當列舉當前訪問節點的子節點的過程中,就可以使用push_back()方法將子節點加入到路徑中,然後往下一層遞迴,最後在下一層回溯上來之後將前面加入的子節點pop_back()即可。
(4)在深度優先遍歷(回溯)的過程中,我們需要記錄當前路徑上的權值和sum。遞迴的過程如下:
a:首先把當前節點push_back()進path中
b:若sum > S,pop_back()後直接return。因為題給的節點權值都是正值,繼續深度優先遍歷得到的sum不可能和S相等,這是一次“剪枝”操作。
c:若sum == S,判斷當前節點是否是葉子節點,如果是葉子節點則列印路徑。無論是否是葉子節點,都需要pop_back()後直接return。
d:若sum < S,我們遍歷當前訪問節點的所有子節點,對每個子節點,進行遞迴呼叫。
e:最後別忘記需要pop_back()對path變數進行手動回溯操作。
(4)sort函式的自定義cmp函式只能用大於或者小於號,不能使用大於等於和小於等於,否則會報段錯誤。
時間複雜度和空間複雜度均是O(N)。
C++程式碼:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct node {
int num;
int weight;
vector<int> child;
};
int N; //節點個數
int M; //非葉子節點個數
int S; //路徑權值
node Node[100]; //建立一個大小為結點上限個數的node型陣列
vector<int> path; //存放路徑
bool cmp(int a, int b);
void dfs(int nowVisit, int sum);
void printPath();
int main(){
cin >> N >> M >> S;
for(int i = 0; i < N; i++){
cin >> Node[i].weight;
}
int ID, K, childID;
for(int i = 0; i < M; i++){
cin >> ID >> K;
for(int j = 0; j < K; j++){
cin >> childID;
Node[ID].child.push_back(childID);
}
sort(Node[ID].child.begin(), Node[ID].child.end(), cmp); //對每個節點的子節點進行排序
}
dfs(0, 0); //從0節點開始深度優先遍歷
return 0;
}
bool cmp(int a, int b){
return Node[a].weight > Node[b].weight; //用大於等於號會出現段錯誤
}
void dfs(int nowVisit, int sum){
path.push_back(nowVisit);
if(sum + Node[nowVisit].weight > S){
path.pop_back();
return;
}
if(sum + Node[nowVisit].weight == S){
if(Node[nowVisit].child.size() == 0){ //判斷當前節點是否是葉子節點
printPath();
}
path.pop_back();
return;
}
for(int i = 0; i < Node[nowVisit].child.size(); i++){
dfs(Node[nowVisit].child[i], sum + Node[nowVisit].weight);
}
path.pop_back();
}
void printPath(){
for(int i = 0; i < path.size(); i++){
cout << Node[path[i]].weight;
if(i != path.size() - 1){
cout << " ";
}
}
cout << endl;
}
C++解題報告: