以一道PAT題爲例,題目:點擊這裏。1053. Path of Equal Weight (30)
題目大意:求從根節點到葉節點權值和等於給定數S的所有路徑,輸出權值。所有的路徑按非遞增輸出。路徑a小於路徑b表示a輸出的權值序列小於b(以第一個不同的值計算)。
思路:
1.考慮到每個節點可能對應多個子節點,用結構體表示節點,vector存放子節點的編號。
2.在讀入子節點時,按權值對子節點排序,以便在遍歷時先輸出權值大的節點序列。
3.使用DFS算法,每次遞歸調用時,權值加上當前節點的權值,若節點爲有效路徑節點,則將其編號加入到path中,若
(1)sum>s,直接return;
(2)sum==s,判斷是否爲子節點;若爲子節點,將其加入到path中,並依次輸出path對應的權值。
(3)sum<s,將節點存入到path中,枚舉當前節點的所有子節點,遞歸調用DFS。
注:這裏思考的問題即是如何存放path,可以想到兩種方法:
(1)使用STL的vector,作爲全局變量。但每次遞歸結束後必須對該變量修改,恢復原值,否則回溯到上一層時變量的值被改變,程序出錯。
代碼如下:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct node {
int w; //權值
vector<int> child;
}no[105];
bool cmp(int a, int b) {
return no[a].w > no[b].w;
}
vector<int> path;
int s;
void DFS(int id, int sum) {
sum += no[id].w;
if (sum > s) {
return;
}
else if (sum == s) {
if (no[id].child.size() == 0) {
path.push_back(id);
for (int i = 0; i < path.size(); i++) {
if (i != path.size() - 1) cout << no[path[i]].w << " ";
else cout << no[path[i]].w << endl;
}
path.pop_back(); //恢復原狀
}
return;
}
else {
path.push_back(id);
for (int i = 0; i < no[id].child.size(); i++) {
DFS(no[id].child[i], sum);
}
path.pop_back(); //恢復原狀
}
}
int main() {
int n, m;
cin >> n >> m >> s;
for (int i = 0; i < n; i++) {
cin >> no[i].w;
}
while (m--) {
int id, k, c;
cin >> id >> k;
for (int i = 0; i < k; i++) {
cin >> c;
no[id].child.push_back(c);
}
sort(no[id].child.begin(), no[id].child.end(), cmp); //每次對子節點的編號根據權值大小進行排序
}
DFS(0, 0); //根節點編號爲0
return 0;
}
(2)同樣使用vector存放,但將其作爲函數參數。因爲遞歸調用函數時,實際上,從內存分佈上看,每一層調用都保存了該層函數的參數,因此遞歸返回上層時,不會影響原參數值。因此將其作爲函數參數遞歸時無需恢復原狀。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct node {
int w; //權值
vector<int> child;
}no[105];
bool cmp(int a, int b) {
return no[a].w > no[b].w;
}
int s;
void DFS(int id, int sum,vector<int> path) {
sum += no[id].w;
if (sum > s) {
return;
}
else if (sum == s) {
if (no[id].child.size() == 0) {
path.push_back(id);
for (int i = 0; i < path.size(); i++) {
if (i != path.size() - 1) cout << no[path[i]].w << " ";
else cout << no[path[i]].w << endl;
}
//path.pop_back(); //無需恢復原狀
}
return;
}
else {
path.push_back(id);
for (int i = 0; i < no[id].child.size(); i++) {
DFS(no[id].child[i], sum, path);
}
//path.pop_back(); //無需恢復原狀
}
}
int main() {
int n, m;
cin >> n >> m >> s;
for (int i = 0; i < n; i++) {
cin >> no[i].w;
}
while (m--) {
int id, k, c;
cin >> id >> k;
for (int i = 0; i < k; i++) {
cin >> c;
no[id].child.push_back(c);
}
sort(no[id].child.begin(), no[id].child.end(), cmp); //每次對子節點的編號根據權值大小進行排序
}
vector<int> p;
DFS(0, 0, p); //根節點編號爲0
return 0;
}
因此,遞歸函數中的變量用函數形參會省去很多麻煩。
另外,當形參爲引用類型時,也需要恢復原樣,因爲對每層遞歸都是使用同一個變量,所以效果上類似於使用全局變量。
修改於2020/3/4~
再看此題,寫是先存儲所有路徑,再輸出的方法:
#include<bits/stdc++.h>
using namespace std;
struct node{
int val;
vector<int> child;
}no[100];
vector<vector<int>> path;
vector<int> temppath;
void dfs(int id,int sum,int &s){
temppath.push_back(no[id].val);
sum+=no[id].val;
if(no[id].child.empty()){
if(sum==s) path.push_back(temppath);
temppath.pop_back();
return;
}
for(int i=0;i<no[id].child.size();i++){
dfs(no[id].child[i],sum,s);
}
temppath.pop_back();
}
int main() {
int n,m,s;
cin>>n>>m>>s;
for(int i=0;i<n;i++) scanf("%d",&no[i].val);
while(m--){
int id,k,temp;cin>>id>>k;
while(k--){
scanf("%d",&temp);
no[id].child.push_back(temp);
}
}
dfs(0,0,s);
sort(path.begin(),path.end());
for(auto it1=path.rbegin();it1!=path.rend();it1++){
auto it2=it1->begin();
for(int i=0;i<it1->size();i++){
if(i!=0) printf(" ");
printf("%d",*(it2+i));
}
printf("\n");
}
return 0;
}