• 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 }

     

    没有人能阻止我前进的步伐,除了我自己!
  • 相关阅读:
    常用256安全色
    PHP获取中英文混合字符串的字数
    windows 2012 443端口无法访问解决随记!
    SQL提取时间段内数据
    正则表达式匹配
    PclZip library能够压缩与解压缩Zip格式
    安装IIS或证书上服务提示安装程序无法复制文件 ftpsvc2.dl的解决办法
    tomcat
    版本控制gitlab
    rsync
  • 原文地址:https://www.cnblogs.com/proverbs/p/2711792.html
Copyright © 2020-2023  润新知