POJ 1848 (一道不错的树形dp)
題意:N個點的一顆樹。問最少添加多少條邊可以讓每個點都在一個(且僅一個)環中。
不得不佩服,這題dp設計出來的人。。。偶是弱菜,只能膜拜了。
這位大牛的解說,很詳細:http://hi.baidu.com/19930705cxjff/blog/item/1df66e4a4ff3022e08f7ef5d.html
?
?
首先明確一點,題中的環至少需要3個頂點。因此,對于樹中的每個頂點,有3種狀態。
f[x][0]表示以x為根的樹,變成每個頂點恰好在一個環中的圖,需要連的最少邊數。
f[x][1]表示以x為根的樹,除了根x以外,其余頂點變成每個頂點恰好在一個環中的圖,需要連的最少邊數。
f[x][2]表示以x為根的樹,除了根x以及和根相連的一條鏈(算上根一共至少2個頂點)以外,其余頂點變成每個頂點恰好在一個環中的圖,需要連的最少邊數。
有四種狀態轉移(假設正在考慮的頂點是R,有k個兒子):
A.根R的所有子樹自己解決(取狀態0),轉移到R的狀態1。即R所有的兒子都變成每個頂點恰好在一個環中的圖,R自己不變。
B.根R的k-1個棵樹自己解決,剩下一棵子樹取狀態1和狀態2的最小值,轉移到R的狀態2。剩下的那棵子樹和根R就構成了長度至少為2的一條鏈。
C.根R的k-2棵子樹自己解決,剩下兩棵子樹取狀態1和狀態2的最小值,在這兩棵子樹之間連一條邊,轉移到R的狀態0。
D.根R的k-1棵子樹自己解決,剩下一棵子樹取狀態2(子樹里還剩下長度至少為2的一條鏈),在這棵子樹和根之間連一條邊,構成一個環,轉移到R的狀態0。
?
?
ps:寫代碼要有膽量,不要怕TLE。。。直接暴力枚舉這些值就行。
附代碼:
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <ctime> #include <queue> #include <map> #include <sstream>#define CL(arr, val) memset(arr, (val), sizeof(arr)) #define REP(i, n) for((i) = 0; (i) < (n); ++(i)) #define FOR(i, l, h) for((i) = (l); (i) <= (h); ++(i)) #define FORD(i, h, l) for((i) = (h); (i) >= (l); --(i)) #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) ((l) + (r)) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) ((x) > 0 ? (x) : -(x))typedef long long LL; const double eps = 1e-12; const int inf = 10000; using namespace std;const int N = 110;struct node {int to;int next; } g[N<<2]; int head[N], t;bool vis[N]; int f[N][3];void init() {CL(head, -1); t = 0; }void add(int u, int v) {g[t].to = v; g[t].next = head[u]; head[u] = t++; }void dfs(int t) {int i, j, k, v, sum = 0, tmp, n;vector<int> ch;for(i = head[t]; i != -1; i = g[i].next) {v = g[i].to;if(!vis[v]) {vis[v] = true;dfs(v);sum += f[v][0];ch.push_back(v);}}n = ch.size();if(n == 0) {//printf("%d here!\n", t);f[t][0] = inf;f[t][1] = 0;f[t][2] = inf;return ;}f[t][1] = min(inf, sum);f[t][0] = f[t][2] = inf;for(i = 0; i < n; ++i) {v = ch[i];f[t][2] = min(f[t][2], sum - f[v][0] + min(f[v][1], f[v][2]));f[t][0] = min(f[t][0], sum - f[v][0] + f[v][2] + 1);}for(i = 0; i < n; ++i) {v = ch[i];for(j = 0; j < n; ++j) {if(i == j) continue;k = ch[j];tmp = sum - f[v][0] - f[k][0] + min(f[v][1], f[v][2]) + min(f[k][1], f[k][2]);f[t][0] = min(f[t][0], tmp + 1);}} }int main() {//freopen("data.in", "r", stdin);int n, x, y, i;while(~scanf("%d", &n)) {init();for(i = 1; i < n; ++i) {scanf("%d%d", &x, &y);add(x, y); add(y, x);}CL(vis, false); vis[1] = true;dfs(1);//for(i = 1; i <= n; ++i) printf("%d | %-11d %-11d %-11d\n", i, f[i][0], f[i][1], f[i][2]);if(f[1][0] >= inf) puts("-1");else printf("%d\n", f[1][0]);}return 0; }?
?
?
?
?
?
轉載于:https://www.cnblogs.com/vongang/archive/2012/08/12/2634763.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的POJ 1848 (一道不错的树形dp)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在Eclipse中写第一个hiberna
- 下一篇: ZOJ 2587 Unique Atta