poj 1848 Tree 树形dp

题意:

给一个n个节点的树加最少的边使得其成为一个环。

分析:

借鉴网上他人的图片分析

首先,我们来分析下对于一个点,我们有哪几种处理方法:一个点i,它在的环可能跟儿子没有关系,也可能跟儿子有关系,但是,可能跟两个儿子有关系,可能仅跟一个儿子有关系,但是不可能跟所有儿子(超过三个)有关系(那样就不符合仅在一个环中了)。 那么我们可以定义三个状态

 dp(i,0)表示当前节点跟它的儿子们全部满足条件的情况下需要加的最少边,显然dp(1,0)就是答案。

dp(i,1)表示当前节点的儿子们全部满足条件的情况下需要加的最少边(即父亲不跟儿子一块成环)。

dp(i,2)表示当前节点跟一个儿子有关系的情况需要加的最少边。(为什么不是两个,因为两个儿子不一定有解,所以在dp(i,0)中对两个儿子进行更新,这里也是为更新dp(i,0)而服务计算的)

有四种状态转移(假设正在考虑的顶点是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。

ACcode:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 10007
#define inf 0x3f3f3f3f
#include <cstring>
using namespace std;
struct N{
    int to,next;
}my[maxn];
int head[maxn],tot;
int dp[maxn][3];
void add(int u,int v){
    my[tot].to=v;
    my[tot].next=head[u];
    head[u]=tot++;
}
void init(){
    memset(head,-1,sizeof(head));
    memset(dp,0,sizeof(dp));
    tot=0;
}
void dfs(int st,int fa){
    int sum=0,min1=inf,min2=inf,minver2=inf;
    for(int i=head[st];i!=-1;i=my[i].next){
        int now=my[i].to;
        if(now!=fa){
            dfs(now,st);
            sum+=dp[now][0];
            minver2=min(minver2,dp[now][2]-dp[now][0]);
            if(min(dp[now][2]-dp[now][0],dp[now][1]-dp[now][0])<min1)
                min2=min1,min1=min(dp[now][2]-dp[now][0],dp[now][1]-dp[now][0]);
            else if(min(dp[now][2]-dp[now][0],dp[now][1]-dp[now][0])<min2)
                min2=min(dp[now][2]-dp[now][0],dp[now][1]-dp[now][0]);
        }
    }
    dp[st][1]=sum;
    dp[st][2]=sum+min1;
    dp[st][0]=sum+1+min(min1+min2,minver2);
}
int main(){
    int n,x,y;
    while(scanf("%d",&n)!=EOF){
        init();
        for(int i=1;i<n;++i){
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
        dfs(1,-1);
        printf("%d\n",dp[1][0]>=inf?-1:dp[1][0]);
    }
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值