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

时间: 2024-08-27 13:34:40

POJ 1848 Tree 树形DP的相关文章

POJ 2486-Apple Tree(树形DP)(难)

题意:一颗树,n个点(1-n),n-1条边,每个点上有一个权值,求从1出发,走V步,最多能遍历到的权值 思路:(思路转自http://blog.csdn.net/libin56842/article/details/10101807) 树形dp,比较经典的一个树形dp.首先很容易就可以想到用dp[root][k]表示以root为根的子树中最多走k时所能获得的最多苹果数,接下去我们很习惯地会想到将k步在root的所有子结点中分配,也就是进行一次背包,就可以得出此时状态的最优解了,但是这里还有一个问

POJ 1741 Tree 树形DP(分治)

链接:id=1741">http://poj.org/problem?id=1741 题意:给出一棵树,节点数为N(N<=10000),给出N-1条边的两点和权值,给出数值k,问树上两点最短距离小于k的点对有多少个. 思路:拿到题的第一反应是LCA问题,只是细一想询问次数极限情况能够达到10000*5000次.即使用Tarjan也是超时没商议的. 2009年国家队论文提供了树的分治思想,对于本题就是树的分治的点分治的应用.每次找到能使含节点最多的子树的节点最少的根分而治之,相同方式分

POJ 2342 (树形DP)

Anniversary party Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 3863   Accepted: 2172 Description There is going to be a party to celebrate the 80-th Anniversary of the Ural State University. The University has a hierarchical structure

Bestcoder round #65 &amp;&amp; hdu 5593 ZYB&#39;s Tree 树形dp

Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 354    Accepted Submission(s): 100 Problem Description ZYB has a tree with N nodes,now he wants you to solve the numbers of nodes distanced no m

codeforces161D - Distance in Tree 树形dp

题意:给你一棵树,问你树中距离为k的有多少种情况. 解题思路:树形dp  维护每个节点(1-K)深度的情况, 解题代码: 1 // File Name: 161d.cpp 2 // Author: darkdream 3 // Created Time: 2014年08月03日 星期日 19时20分10秒 4 5 #include<vector> 6 #include<list> 7 #include<map> 8 #include<set> 9 #incl

hdu5593/ZYB&#39;s Tree 树形dp

ZYB's Tree Memory Limit: 131072/131072 K (Java/Others) 问题描述 ZYBZYB有一颗NN个节点的树,现在他希望你对于每一个点,求出离每个点距离不超过KK的点的个数. 两个点(x,y)(x,y)在树上的距离定义为两个点树上最短路径经过的边数, 为了节约读入和输出的时间,我们采用如下方式进行读入输出: 读入:读入两个数A,BA,B,令fa_ifa?i??为节点ii的父亲,fa_1=0fa?1??=0;fa_i=(A*i+B)\%(i-1)+1fa

URAL_1018 Binary Apple Tree 树形DP+背包

这个题目给定一棵树,以及树的每个树枝的苹果数量,要求在保留K个树枝的情况下最多能保留多少个苹果 一看就觉得是个树形DP,然后想出 dp[i][j]来表示第i个节点保留j个树枝的最大苹果数,但是在树形过程中,有点难表示转移 后来看了下大神的做法才知道其实可以用背包来模拟 树枝的去留,其实真的是个背包诶,每个子树枝就相当于物品,他占用了多少树枝量,带来多少的收益,就是用背包嘛,于是用树形DP+背包就可以做了 #include <iostream> #include <cstdio> #

POJ 1848 Tree

Tree Time Limit: 1000ms Memory Limit: 30000KB This problem will be judged on PKU. Original ID: 184864-bit integer IO format: %lld      Java class name: Main Consider a tree with N vertices, numbered from 1 to N. Add, if it is possible, a minimum numb

Codeforces 461B Appleman and Tree(树形dp)

题目链接:Codeforces 461B Appleman and Tree 题目大意:一棵树,以0节点为根节点,给定每个节点的父亲节点,以及每个点的颜色(0表示白色,1表示黑色),切断这棵树的k条边,使得树变成k+1个联通分量,保证每个联通分量有且仅有1个黑色节点.问有多少种分割方法. 解题思路:树形dp,dp[i][0]和dp[i][1]分别表示子树一下的分割方法中,i节点所在联通块不存在黑节点和已经存在一个黑节点的方案数. #include <cstdio> #include <c