【洛谷】P1137 旅行計劃
0.總結
Get to the points first. The article comes from LawsonAbs!
- 樹形dp問題的處理
- 拓撲排序
1.題意
其實就是求一棵樹裏各個節點的子節點個數。
2.分析
如果單純的使用dfs+dp的方法那麼在面對多叉的情況下就會失效。其原因是:會包含重複的節點【用術語來說就是,一個節點有不止一個父節點】。那麼該怎麼解決這個問題呢? 可以化樹形序列爲dp序列,然後在得到線型序列【放到數組裏】的基礎上進行dp計算。
主要步驟如下:
- 按照由西到東【或者由東到西也是可以的,建圖什麼順序,就決定了後面的dp該以什麼順序更新值】進行一個建圖的操作
- 將樹形結構進行一個拓撲排序,將排序的結果放到一個數組
topo[]
中,並按照得到的順序 - 將上面得到的
topo[]
結構進行一個dp計算,取子節點中的最大值
3.代碼
3.1 錯誤代碼
下面這份代碼,則是
// Created by lawson on 20-6-16.
#include<iostream>
using namespace std;
const int maxM = 200005;
const int maxN = 100005;
//head[i]表示節點i 指向的第一個節點
//next[i] 表示第i條辺的開始節點
int head[maxM],nex[maxM],to[maxM];
int n,m,cnt = 0;//cnt 表示辺的個數
int ro[maxN];
int dp[maxN];//dp[i]表示節點i能夠遊覽最多的城市數
//添加辺的過程 a->b
void add(int a,int b){
nex[cnt] = head[a];
to[cnt] = b;//第cnt條辺指向的終點
head[a] = cnt; //節點a指向的第一條邊的下標
cnt++;
}
/*
01.如果不先拓撲排序,那麼對於一個分叉的節點就無法得到正確答案
*/
//當前節點cur
void dfs(int cur){
int y;//目的節點
for(int i = head[cur];i!=-1;i = nex[i]){ //這裏遍歷得到的i是辺的序號
y = to[i];
dfs(y);//繼續往下遍歷
dp[cur] = max(dp[cur],dp[y]);
}
dp[cur]+=1;//加上自己的一個節點
}
int main(){
cin >> n >> m;
int l,r;//左右兩個節點。 l在r的西面
fill(head,head+maxM,-1);//以-1結尾
for(int i = 0;i< m;i++){
cin >> l >> r;
ro[l] = 1;
add(r,l);//反着加
}
for(int i = 1;i<=n;i++){
if(!ro[i]) //如果該點是
dfs(i);
}
for(int i = 1;i<=n;i++)
cout << dp[i]<<"\n";
return 0;
}
3.2 ac代碼
待補坑。