POJ 1848 (一道不错的树形dp)
題意:N個(gè)點(diǎn)的一顆樹(shù)。問(wèn)最少添加多少條邊可以讓每個(gè)點(diǎn)都在一個(gè)(且僅一個(gè))環(huán)中。
不得不佩服,這題dp設(shè)計(jì)出來(lái)的人。。。偶是弱菜,只能膜拜了。
這位大牛的解說(shuō),很詳細(xì):http://hi.baidu.com/19930705cxjff/blog/item/1df66e4a4ff3022e08f7ef5d.html
?
?
首先明確一點(diǎn),題中的環(huán)至少需要3個(gè)頂點(diǎn)。因此,對(duì)于樹(shù)中的每個(gè)頂點(diǎn),有3種狀態(tài)。
f[x][0]表示以x為根的樹(shù),變成每個(gè)頂點(diǎn)恰好在一個(gè)環(huán)中的圖,需要連的最少邊數(shù)。
f[x][1]表示以x為根的樹(shù),除了根x以外,其余頂點(diǎn)變成每個(gè)頂點(diǎn)恰好在一個(gè)環(huán)中的圖,需要連的最少邊數(shù)。
f[x][2]表示以x為根的樹(shù),除了根x以及和根相連的一條鏈(算上根一共至少2個(gè)頂點(diǎn))以外,其余頂點(diǎn)變成每個(gè)頂點(diǎn)恰好在一個(gè)環(huán)中的圖,需要連的最少邊數(shù)。
有四種狀態(tài)轉(zhuǎn)移(假設(shè)正在考慮的頂點(diǎn)是R,有k個(gè)兒子):
A.根R的所有子樹(shù)自己解決(取狀態(tài)0),轉(zhuǎn)移到R的狀態(tài)1。即R所有的兒子都變成每個(gè)頂點(diǎn)恰好在一個(gè)環(huán)中的圖,R自己不變。
B.根R的k-1個(gè)棵樹(shù)自己解決,剩下一棵子樹(shù)取狀態(tài)1和狀態(tài)2的最小值,轉(zhuǎn)移到R的狀態(tài)2。剩下的那棵子樹(shù)和根R就構(gòu)成了長(zhǎng)度至少為2的一條鏈。
C.根R的k-2棵子樹(shù)自己解決,剩下兩棵子樹(shù)取狀態(tài)1和狀態(tài)2的最小值,在這兩棵子樹(shù)之間連一條邊,轉(zhuǎn)移到R的狀態(tài)0。
D.根R的k-1棵子樹(shù)自己解決,剩下一棵子樹(shù)取狀態(tài)2(子樹(shù)里還剩下長(zhǎng)度至少為2的一條鏈),在這棵子樹(shù)和根之間連一條邊,構(gòu)成一個(gè)環(huán),轉(zhuǎn)移到R的狀態(tài)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; }?
?
?
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/vongang/archive/2012/08/12/2634763.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的POJ 1848 (一道不错的树形dp)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 在Eclipse中写第一个hiberna
- 下一篇: ZOJ 2587 Unique Atta