POJ 1848 树形DP

这个提出的真心非常好!YM!

题意:

给定一棵无根树,问至少需要添加多少条边,使得每个节点属于且仅属于一个圈,并且,每个圈的节点数至少为3。若无解则输出-1,否则输出至少添加的边数。

 

题解:

如果不知道这个是TreeDP的话或者我大概会往图论方面想的,求SCC啊之类的。

自己YY的时候没有注意到,如果是两个子链合成一个圈的话,那么根节点一定是属于该圈,于是一直在郁闷怎么分类讨论若干种转移,毕竟这种写法还是太奇葩了。

一开始我的状态设计是:

  1. f[0, u]表示根节点u属于某个圈,且以u为根的整棵子树都符合要求,所需要添加的最少边数。
  2. f[1, u]表示根节点u属于某条链,且以u为根的整棵子树除了该链之外都符合要求,所需要添加的最少边数。

然后再用一个tag[]来表示这条链的长度是否大于1。= = 但,很不幸地,它WA了。而且无论是随机生成数据对拍亦或者是构造数据,都是FC: no differences encountered,应该是哪个小细节没写对吧,于是只能把这种非主流写法改成主流的写法啦。>_<

  1. f[0, x]表示以x为根的树,变成每个顶点恰好在一个圈中的图,需要添加的最少边数。
  2. f[1, x]表示以x为根的树,除了根x以外,其余节点变成每个节点恰好在一个圈中的图,需要添加的最少边数。
  3. f[2, x]表示以x为根的树,除了根x以及其所在的一条链(*链长度要大于1)以外,其余节点变成每个节点恰好在一个圈中的图,需要添加的最少边数。

于是就有:
i,j,vthesetofxsson,ij
f[0, x] = min{
sum{f[0, v]} – max{f[0, i] – f[2, i]} ,
sum{f[0, v]} – max{f[0, i] – min{f[1, i], f[2, i]} – max{f[0, j] – min{f[1, j], f[2, j]}}
}
f[1, x] = sum{f[0, v]}
f[2, x] = sum{f[0, v]} – max{f[0, i] – min{f[2, i], f[1, i]}}
或者参考Felicia的solution:

A.根R的所有子树自己解决(取状态0),转移到R的状态1。即R所有的儿子都变成每个顶点恰好在一个环中的图,R自己不变。
1
B.根R的k-1个棵树自己解决,剩下一棵子树取状态1和状态2的最小值,转移到R的状态2。剩下的那棵子树和根R就构成了长度至少为2的一条链。
2
C.根R的k-2棵子树自己解决,剩下两棵子树取状态1和状态2的最小值,在这两棵子树之间连一条边,转移到R的状态0。
3
D.根R的k-1棵子树自己解决,剩下一棵子树取状态2(子树里还剩下长度至少为2的一条链),在这棵子树和根之间连一条边,构成一个环,转移到R的状态0。
4

(上文引用自:http://abyss.ylen.me/archives/4

 

由于一看这题有环就没思路,我是看题解的,看完了就后悔了,这个完全可以自己想到的!我该死。。

 

View Code
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <iostream>
 5 
 6 #define N 10000
 7 
 8 using namespace std;
 9 
10 int to[N],next[N],head[N],cnt,n,dp[N][3];
11 
12 inline void add(int u,int v)
13 {
14     to[cnt]=v; next[cnt]=head[u]; head[u]=cnt++;
15 }
16 
17 void read()
18 {
19     memset(head,-1,sizeof head);cnt=0;
20     for(int i=1,a,b;i<n;i++)
21     {
22         scanf("%d%d",&a,&b);
23         add(a,b); add(b,a);
24     }
25 }
26 
27 void find(int u,int fa)
28 {
29     int sum=0;
30     for(int i=head[u];~i;i=next[i])
31         if(to[i]!=fa)
32         {
33             find(to[i],u);
34             sum+=dp[to[i]][0];
35         }
36     dp[u][1]=sum;
37     for(int i=head[u];~i;i=next[i])
38         if(to[i]!=fa)
39         {
40             dp[u][0]=min(dp[u][0],sum-dp[to[i]][0]+dp[to[i]][2]+1);
41             dp[u][2]=min(dp[u][2],sum-dp[to[i]][0]+min(dp[to[i]][1],dp[to[i]][2]));
42             for(int j=head[u];~j;j=next[j])
43                 if(to[j]!=fa&&to[i]!=to[j])
44                     dp[u][0]=min(dp[u][0],sum-dp[to[i]][0]-dp[to[j]][0]+min(dp[to[i]][1],dp[to[i]][2])+min(dp[to[j]][1],dp[to[j]][2])+1);
45         }
46 }
47 
48 void go()
49 {
50     for(int i=1;i<=n;i++) dp[i][0]=dp[i][1]=dp[i][2]=999999;
51     find(1,-1);
52     if(dp[1][0]>99999) dp[1][0]=-1;
53     printf("%d\n",dp[1][0]);
54 }
55 
56 int main()
57 {
58     while(scanf("%d",&n)!=EOF)
59     {
60         read();
61         go();
62     }
63     return 0;
64 }

 

 

 

posted @ 2012-10-04 22:03  proverbs  阅读(485)  评论(0编辑  收藏  举报