UVa 1218 樹形DP
阿新 • • 發佈:2019-02-07
思路主要是紫書 上的,我在這裡說一下建樹的細節。
#include <vector> #include <cstdio> #include <algorithm> #include <cstring> #include <set> using namespace std; vector<int> son[10005]; int dp[10005][3]; int vis[10005]; //int have[10005]; const int INF = 1<<29; void search(int u) { vis[u] = 1; dp[u][0] = 0; dp[u][1] = 0; dp[u][2] = 1; if(son[u].empty()) { dp[u][0] = INF; return ; } for(int i = 0; i < son[u].size(); i++) { if(vis[son[u][i]]) continue; //從樹根開始向下遍歷,一旦這個節點用過了,就不能再用了,否則就會出現遍歷一個節點的兒子時遍歷到這個節點的父親的情況 if(!vis[son[u][i]]) search(son[u][i]); dp[u][1] += dp[son[u][i]][0]; dp[u][2] += min(dp[son[u][i]][2], dp[son[u][i]][1]); } int sum = 0; for(int i = 0; i < son[u].size(); i++) sum += dp[son[u][i]][0]; dp[u][0] = sum + dp[son[u][0]][2] - dp[son[u][0]][0]; for(int i = 0; i < son[u].size(); i++) dp[u][0] = min(dp[u][0], sum + dp[son[u][i]][2] - dp[son[u][i]][0]); return ; } int main() { //freopen("ztest.txt","r",stdin); //freopen("zans.txt","w",stdout); int n, root; int flagg = 1; while(scanf("%d",&n)) { for(int i = 0; i <= n; i++) son[i].clear(); int flag = 1, temp1, temp2; memset(vis, 0, sizeof(vis)); //memset(have, 0, sizeof(have)); while(scanf("%d",&temp1) && temp1) { if(flag == 1) { root = temp1; flag = 0; } if(temp1 == -1) {flagg = 0; break;} scanf("%d",&temp2); son[temp1].push_back(temp2); //這時建立的樹中,兒子的兒子中有父親,vis[]解決了這個問題 son[temp2].push_back(temp1); } search(root); //for(int i = 1; i <= n; i++) // printf("%d %d %d\n",dp[i][0],dp[i][1],dp[i][2]); printf("%d\n",min(dp[root][2], dp[root][0])); if(!flagg) break; } return 0; }