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自己不变。

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。

(上文引用自: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 }

 

 

 

你可能感兴趣的:(poj)