1. 程式人生 > >PAT-ADVANCED1053——Path of Equal Weight

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},均有A​i​​ = B​i​​,且A​k+1​​ > B​k+1​​,則序列{A​1​​,A​2​​,⋯,A​n​​}被定義為比序列{B​1​​,B​2​​,⋯,B​m​​}要大。

輸入樣例:

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++解題報告: