poj Tree(树形dp)

Tree

Time Limit: 1000MS   Memory Limit: 30000K
Total Submissions: 14706   Accepted: 4781

Description

Give a tree with n vertices,each edge has a length(positive integer less than 1001).

Define dist(u,v)=The min distance between node u and v.

Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k.

Write a program that will count how many pairs which are valid for a given tree.

Input

The input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l, which means there is an edge between node u and v of length l.

The last test case is followed by two zeros.

Output

For each test case output the answer on a single line.

Sample Input

5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0

Sample Output

8

Source

[email protected]

题意:给定n个点的树,求其中任意两个点的距离<=k的对数。

分析:分为两种情况,一种是不同支的情况,这种好处理一些,直接往上找到公共父亲权值相加就可以;另一种就是同支的情况,把重复的权值删去就行了,只是实现起来麻烦些。另外推荐一篇博客详解,我也是看的他的,很厉害。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
const double eps = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int MOD = 1000000007;
#define ll long long
#define CL(a,b) memset(a,b,sizeof(a))
#define MAXN 20010

struct node
{
    int v,len;//v是邻接点,len是权值
    int sum,bat;//sum是子节点个数,bat是拆当前点后子树的最大结点数
    node *next;
}tree[MAXN],*head[MAXN],dp[MAXN];
int n,k,ans,pre,root,tot;
int vis[MAXN],dist[MAXN];
int size[MAXN],sign[MAXN];//size表示最大分支的结点数,sign是一个hash数组  

void init()
{
    ans = pre = 0;
    for(int i=0; i<MAXN; i++)
        vis[i]=0, head[i]=NULL;
}

void add(int a, int b, int c)
{
    tree[pre].v = b; tree[pre].len = c;
    tree[pre].next = head[a]; head[a] = &tree[pre++];
}

void dfs(int son, int father)
{
    dp[son].sum = dp[son].bat = 0;
    node *p = head[son];
    while(p != NULL)
    {
        if(p->v!=father&&vis[p->v]==0)
        {
            dfs(p->v, son);
            dp[son].sum += dp[p->v].sum;//累计子节点数
            dp[son].bat = max(dp[son].bat, dp[p->v].sum);//找最大分支
        }
        p = p->next;
    }
    dp[son].sum++;
    sign[tot] = son;//hash
    size[tot++] = dp[son].bat;//记录每个最大分支的结点数
}

int GetRoot(int son)
{
    tot = 0; dfs(son, 0);
    int maxx=INF, maxi, cnt=dp[son].sum;
    for(int i=0; i<tot; i++)
    {
        size[i] = max(size[i], cnt-size[i]);
        if(size[i] < maxx)
        {
            maxx = size[i];
            maxi = sign[i];
        }
    }
    return maxi;
}

void GetDist(int son, int father, int dis)//保存每个结点到根结点的距离
{
    node *p = head[son];
    dist[tot++] = dis;
    while(p != NULL)
    {
        if(p->v!=father&&vis[p->v]==0&&dis+p->len<=k)
            GetDist(p->v, son, dis+p->len);
        p = p->next;
    }
}

void count1(int son)//不同支
{
    sort(dist, dist+tot);
    int left=0, right=tot-1;
    while(left < right)
    {
        if(dist[left]+dist[right] <= k)
            ans += (right - left), left++;
        else right--;
    }
}

void count2(int son)//同支
{
    vis[son] = 1;
    node *p = head[son];
    while(p != NULL)
    {
        if(vis[p->v] == 0)
        {
            tot = 0; GetDist(p->v, son, p->len);
            sort(dist, dist+tot);
            int left=0, right=tot-1;
            while(left < right)
            {
                if(dist[left]+dist[right] <= k)
                    ans -= (right - left), left++;
                else right--;
            }
        }
        p = p->next;
    }
}

int solve(int son, int father)
{
    root = GetRoot(son);
    tot=0;
    GetDist(root, 0, 0);
    count1(root);
    count2(root);
    node *p = head[root];
    while(p != NULL)
    {
        if(p->v!=father&&vis[p->v]==0)
            solve(p->v, root);
        p = p->next;
    }
}

int main()
{
    int a,b,c;
    while(scanf("%d%d",&n,&k),n+k)
    {
        init();
        for(int i=1; i<n; i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            add(a, b, c);
            add(b, a, c);
        }
        solve(1, 0);
        printf("%d\n",ans);
    }
    return 0;
}
时间: 2024-10-10 11:14:17

poj Tree(树形dp)的相关文章

poj 3342(树形dp)

题意:在一个公司中要举办一个聚会,每一个员工有一个奉献值.为了和谐规定直接上下级不能一起出席.让你找出奉献值之和最大为多少. 思路:dp[v][1]表示当前结点选,能获得的最大奉献值,dp[v][0]表示当前节点不选能获得的最大奉献值.状态转移: dp[v][0] = max(dp[v][0], ∑max(dp[x][1], dp[x][0]))x为直接儿子 dp[v][1] = max(dp[v][1], ∑dp[x][0] + vex[v]) 最后答案是max(dp[root][0], dp

poj 1947(树形dp)

题意:一棵树上问你最少切掉几条边使得能分割出一个结点数正好为k的子树. 思路:dp[i][j]表示以i为根切掉j个结点最少要几条边. dp[v][j] = min(dp[v][j], dp[v][j-k] + dp[x][k]); 代码如下: 1 dp[v][j] = min(dp[v][j], dp[v][j-k] + dp[x][k]); 2 } 3 } 4 } 5 } 6 } 7 return vex[v]; 8 } 9 10 int main() 11 { 12 // freopen("

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

POJ 2486 树形DP

有一颗苹果树,每个节点上面有很多苹果,从一个节点到另外一个可以到达的节点花费1步,求k步最多能吃到多少苹果,起始点为1,可以不回到起始点. 这是典型的回溯型树状dp. dp[i][j][0]代表以i为根节点的子树最多j步后回到i能吃到的最多的苹果, dp[i][j][1]代表以i为根节点的子树最多j步后不回到i节点最多能吃到的子树.那么状态转移就分三步了. (1)dp[i][j+2][0] = max(dp[i][j+2][0], dp[i][j-k][0]+dp[son][k][0]); (2

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

POJ 3342 树形DP入门题

题目意思和POJ2342一样,只是多加了一个条件,如果最大方案数唯一,输出Yes,不唯一输出No dp的是时候多加一个变量记录答案是否唯一即可 #include "stdio.h" #include "string.h" #include "vector" using namespace std; struct node { int fa; vector<int>child; }data[210]; struct comp { int

POJ 1947 树形DP入门题

给出N个点,N-1个关系,建出树形图,问最少减去几个边能得到节点数为P的树.典型树形DP题 dp[cur][j] :记录cur结点,要得到一棵j个节点的子树去掉的最少边数 转移方程用的背包的思想 对当前树的每一个子树进行计算 砍掉此子树:   dp[cur][j]=dp[cur][j]+1; 不砍掉:           for (l=0;l<=j;l++)  dp[cur][j]=Min(dp[cur][j],dp[cur][l]+dp[next][j-l]); 枚举从该树中留l个节点其他由新

URAL_1018 Binary Apple Tree 树形DP+背包

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

POJ 2342 树形DP入门题

有一个大学的庆典晚会,想邀请一些在大学任职的人来参加,每个人有自己的搞笑值,但是现在遇到一个问题就是如果两个人之间有直接的上下级关系,那么他们中只能有一个来参加,求请来一部分人之后,搞笑值的最大是多少. 树形DP入门题. DP部分: dp[i][0]表示职员i不来参加party,以i为根的子树的最大搞笑值, dp[i][1]表示职员i来参加party,以i为根的子树的最大搞笑值. 转移方程: dp[cur][1]+=dp[next][0]; dp[cur][0]+=Max(dp[next][1]