POJ 1848 Tree 树形DP

哆啦A梦的时空隧道


题目大意:
给出一棵n个点的树,求出最少添加几条边可以使得这颗树上的每个节点都在且仅在一个环内…..(题目比较坑人,没有说清楚这个环至少是三个节点)
如果无法满足,输出-1


分析:
想了很久还是没有思路……..
去看了一下状态是什么…..
然后自己试着推了推DP方程……ms是对的….
f[i][0]代表以i为根的子树每个顶点都在且仅在一个环中的ans
f[i][1]代表以i为根的子树除去根节点外每个顶点都在且仅在一个环中的ans
f[i][2]代表以i为根的子树除去根节点以及与根相连的一条链之外(也就是加上根节点至少有2个vertices)的ans
然后考虑每个根节点的状态可以由子节点的哪些状态转移而来…..
f[root][1]=∑f[to[i]][0]这是最显然的一个
f[root][2]=f[to[i]][0]*(k-1)+min(f[to[i]][1],f[to[i]][2])
就是说在root的k个子节点中,有k-1个都已经变成了满足题目要求的图,还有一颗子树有一个顶点或者一条链没有在环中,那么这条链或者这个顶点就与root组成了新的一条链
f[root][0]=min(A,B)
A=f[to[i]][0]*(k-1)+f[to[i]][2]+1
如图:(画的不太好(✿◡‿◡))
画的不太好(✿◡‿◡)
B=f[to[i]][0]*(k-2)+min(f[to[i]][2],f[to[i]][1])*2+1
大概就是有两条链,然后往中间添一条边……


代码如下:

//by NeighThorn
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=100+5;
int n,hd[maxn],to[maxn*2],nxt[maxn*2],cnt,f[maxn][3];
inline int read(void){
    char ch=getchar();int f=1,x=0;
    while(!(ch>='0'&&ch<='9')){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
        x=x*10+ch-'0',ch=getchar();
    return f*x; 
}
inline void add(int x,int y){
    to[cnt]=y;
    nxt[cnt]=hd[x];
    hd[x]=cnt++;
}
//f[i][0]代表以i为根的子树每个顶点都在且仅在一个环中的ans
//f[i][1]代表以i为根的子树除去根节点外每个顶点都在且仅在一个环中的ans
//f[i][2]代表以i为根的子树除去根节点以及与根相连的一条链之外(也就是加上根节点至少有2个vertices)的ans
//f[i][1]=∑(k)f[to[i]][0]
//f[i][2]=∑(k-1)f[to[i]][0]   +(1)min(f[to[i]][1],f[to[i]][2])
//f[i][0]=∑(k-2)f[to[i]][0]   +(2)min(f[to[i]][1],f[to[i]][2])+1
//f[i][0]=∑(k-1)f[to[i]][0]   +(1)f[to[i]][2]+1
inline void dfs(int root,int fa){
    int sum=0,min1=inf,min2=inf,minver2=inf;
    for(int i=hd[root];i!=-1;i=nxt[i])
        if(to[i]!=fa){
            dfs(to[i],root);
            sum+=f[to[i]][0];
            minver2=min(minver2,f[to[i]][2]-f[to[i]][0]);
            if(min(f[to[i]][2]-f[to[i]][0],f[to[i]][1]-f[to[i]][0])<min1)
                min2=min1,min1=min(f[to[i]][2]-f[to[i]][0],f[to[i]][1]-f[to[i]][0]);
            else if(min(f[to[i]][2]-f[to[i]][0],f[to[i]][1]-f[to[i]][0])<min2)
                min2=min(f[to[i]][2]-f[to[i]][0],f[to[i]][1]-f[to[i]][0]);
        }
    f[root][1]=sum;
    f[root][2]=sum+min1;
    f[root][0]=sum+1+min(min1+min2,minver2);
}
signed main(void){
    while(scanf("%d",&n)!=EOF){
        cnt=0,memset(hd,-1,sizeof(hd));
        for(int i=1,x,y;i<n;i++)
            x=read(),y=read(),add(x,y),add(y,x);
        memset(f,0,sizeof(f)),dfs(1,-1);
        cout<<(f[1][0]>=inf?-1:f[1][0])<<endl;
    }
    return 0;
}

by >_< neighthorn

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值