HDU 4123(树的直径+单调队列)

Bob’s Race

Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2833    Accepted Submission(s): 917


Problem Description
Bob wants to hold a race to encourage people to do sports. He has got trouble in choosing the route. There are N houses and N - 1 roads in his village. Each road connects two houses, and all houses are connected together. To make the race more interesting, he requires that every participant must start from a different house and run AS FAR AS POSSIBLE without passing a road more than once. The distance difference between the one who runs the longest distance and the one who runs the shortest distance is called “race difference” by Bob. Bob does not want the “race difference”to be more than Q. The houses are numbered from 1 to N. Bob wants that the No. of all starting house must be consecutive. He is now asking you for help. He wants to know the maximum number of starting houses he can choose, by other words, the maximum number of people who can take part in his race.
 

 

Input
There are several test cases.
The first line of each test case contains two integers N and M. N is the number of houses, M is the number of queries.
The following N-1 lines, each contains three integers, x, y and z, indicating that there is a road of length z connecting house x and house y.
The following M lines are the queries. Each line contains an integer Q, asking that at most how many people can take part in Bob’s race according to the above mentioned rules and under the condition that the“race difference”is no more than Q. 

The input ends with N = 0 and M = 0.

(N<=50000 M<=500 1<=x,y<=N 0<=z<=5000 Q<=10000000)
 

 

Output
For each test case, you should output the answer in a line for each query.
 

 

Sample Input
5 5 1 2 3 2 3 4 4 5 3 3 4 2 1 2 3 4 5 0 0
 

 

Sample Output
1 3 3 3 5
 

 

Source
 
一、求树的直径问题
     树的直径:树中距离最远的两点间的距离。
     1、树形dp
      首先建树,新学的建树方式,一般我都是直接vector的存点的。
      然后dfs求出每个节点到所有叶子的最大距离。
      最重要的一步,求出跟到除当前子节点之外的所有分支的最大距离,然后加上跟到当前子节点的距离,向上更新。
      先贴一份别人的代码:
      Hdu2196  
#include <stdio.h>
#include <string.h>
#define MAX 10001
#define max(a,b) (a)>(b)?(a):(b)


struct node {

    int v,len,sum;
    node *next;
}*head[MAX*2],tree[MAX*2];
__int64 dp[MAX];
int n,ptr,vis[MAX];



void Initial() {

    ptr = 1;
    memset(dp,0,sizeof(dp));
    memset(vis,0,sizeof(vis));
    memset(head,NULL,sizeof(head));
}
void AddEdge(int x,int y,int len) {

    tree[ptr].v = y,tree[ptr].len = len;
    tree[ptr].next = head[x],head[x] = &tree[ptr++];
    //printf("ptr : %d **** tree[ptr].v: ")
    tree[ptr].v = x,tree[ptr].len = len;
    tree[ptr].next = head[y],head[y] = &tree[ptr++];
}
void Dfs(int v) {

    vis[v] = 1;
    node *p = head[v];
    
    while (p != NULL) {

        if (!vis[p->v]) {

            Dfs(p->v);
            dp[v] = max(dp[v],dp[p->v]+p->len);
            p->sum = dp[p->v] + p->len;
        }
        p = p->next;
    }
}
void Tree_DP(int pa,int son) {

    if (vis[son]) return;
    vis[son] = 1;
    int i,j,k,maxx = 0;

    node *p = head[pa];
    while (p != NULL) {
        //找到父节点除son外其他分支的最大价值
        if (p->v != son)
            maxx = max(maxx,p->sum);
        p = p->next;
    }

    p = head[son];
    while (p != NULL) {

        if (p->v == pa) {
        //这一步至关重要,往上更新,才能保证每步都得到最优解
            p->sum = p->len + maxx;
            break;
        }
        p = p->next;
    }


    p = head[son];
    while (p != NULL) {
        //每次都更新当前节点,并往下递归计算,父节点会因为vis=1而不计算
        dp[son] = max(dp[son],p->sum);
        Tree_DP(son,p->v);
        p = p->next;
    }
}


int main()
{
    int i,j,k,a,b;


    while (scanf("%d",&n) != EOF) {

        Initial();
        for (i = 2; i <= n; ++i) {

            scanf("%d%d",&a,&b);
            AddEdge(i,a,b);
        }


        Dfs(1);
        memset(vis,0,sizeof(vis));
        node *p = head[1];
        while (p != NULL) {

            Tree_DP(1,p->v);
            p = p->next;
        }
        for (i = 1; i <= n; ++i)
            printf("%I64d\n",dp[i]);
    }
}
View Code

   还有一种其他的理解,这种我感觉比较好理解的。

   先建一棵有根数。

   求出每个节点到叶子的最长距离和次长距离,并记录到最长距离要经过的子节点(与其相连那一个)。

   接下来分两种情况:

   (1)当前节点的最长距离经过某子节点,则某子节点的最长距离为当前节点的次长距离和某子节点的最长距离的最大值加当前节点到某子节点的距离

   (2)当前节点的最长距离不经过某子节点,则某子节点的最长距离为当前节点的最长距离和某子节点的最长距离的最大值加当前节点到某子节点的距离

    更新子节点

   hdu2196

#include <cstdio>
#include <iostream>
#include <sstream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;
#define ll long long
#define _cle(m, a) memset(m, a, sizeof(m))
#define repu(i, a, b) for(int i = a; i < b; i++)
#define repd(i, a, b) for(int i = b; i >= a; i--)
#define sfi(n) scanf("%d", &n)
#define sfl(n) scanf("%I64d", &n)
#define pfi(n) printf("%d\n", n)
#define pfl(n) printf("%I64d\n", n)
#define MAXN 1000005

struct Node
{
    int v, len;
    Node* next;
}*head[MAXN], tree[MAXN * 2];
bool vis[MAXN];
int c = 0;
int last[MAXN];
ll maxlen[MAXN], smaxlen[MAXN];
ll dp[MAXN];
void init()
{
    c = 1;
    _cle(vis, 0);
    _cle(head, NULL);
    _cle(dp, 0);
    _cle(last, 0);
    return ;
}

void Add_Edge(int x, int y, int len)
{
    tree[c].v = y, tree[c].len = len;
    tree[c].next = head[x], head[x] = &tree[c++];
    tree[c].v = x, tree[c].len = len;
    tree[c].next = head[y], head[y] = &tree[c++];
}

void dfs(int fa, int pre)
{
    if(vis[fa]) return ;
    vis[fa] = 1;
    Node* p = head[fa];
    while(p != NULL)
    {
        dfs(p -> v, fa);
        p = p -> next;
    }
    int maxn = 0;
    p = head[fa];
    while(p != NULL)
    {
        if(maxn < p -> len + maxlen[p -> v] && p -> v != pre)
        {
            maxn = p -> len + maxlen[p -> v];
            last[fa] = p -> v;
        }
        p = p -> next;
    }
    maxlen[fa] = maxn;
    maxn = 0;
    p = head[fa];
    while(p != NULL)
    {
        if(p -> v != pre && p -> v != last[fa] && maxn < p -> len + maxlen[p -> v])
            maxn = p -> len + maxlen[p -> v];
        p = p -> next;
    }
    smaxlen[fa] = maxn;
    return ;
}

void DP(int fa)
{
    vis[fa] = 1;
    Node* p = head[fa];
    while(p != NULL)
    {
        if(!vis[p -> v])
        {
            if(last[fa] != p -> v)
                dp[p -> v] = max(dp[fa], maxlen[fa]) + p -> len;
            else
                dp[p -> v] = max(dp[fa], smaxlen[fa]) + p -> len;
            DP(p -> v);
        }
        p = p -> next;
    }
    return ;
}

int main()
{
    int n;
    while(~sfi(n))
    {
        init();
        int x, y, len;
        repu(i, 2, n + 1)
        {
            sfi(y), sfi(len);
            Add_Edge(i, y, len);
        }

        dfs(1, -1);
        _cle(vis, 0);
        DP(1);
        repu(i, 1, n + 1)
        {
            // pfl(smaxlen[i]);
            pfl(max(maxlen[i], dp[i]));
        }
    }
    return 0;
}
View Code

 

 2、利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点。这样就可以首先任取一个点,bfs求出离其最远的点,在用同样的方法求出离这个叶子节点最远的点,此时两点间的距离就是树的直径。 
Poj1985
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 40005;
vector<int> son[maxn], w[maxn];
bool vis[maxn], viss[maxn];
int f[maxn];
int bfs(int root){
    int i, j, k;
    int ans = root, maxx = 0;
    queue<int> q;
    memset(vis,0,sizeof(vis));
    memset(f,0,sizeof(f));
    q.push(root);
    vis[root] = 1;f[root] = 0;viss[root] = 1;
    while(!q.empty()){
        root = q.front();
        q.pop();
        for(i=0;i<son[root].size();i++){
            if(vis[son[root][i]]==0){
                q.push(son[root][i]);
                vis[son[root][i]] = 1;viss[son[root][i]] = 1;
                f[son[root][i]] = f[root]+w[root][i];
                if(maxx<f[son[root][i]]){
                    maxx = f[son[root][i]];
                    ans = son[root][i];
                }
            }
        }
    }
    return ans;
}
int solve(int root){
    int  u, v;
    u = bfs(root);
    v = bfs(u);
    return f[v];
}
int main(){
    int i, j, k, n, m;
    int x1, x2, l, u;
    int res;
    char opt;
    while(~scanf("%d%d",&n,&m)){
        for(i=0;i<=n;i++){
            son[i].clear();
            w[i].clear();
        }
        for(i=0;i<m;i++){
            scanf("%d%d%d",&x1,&x2,&l);
            scanf(" %c",&opt);
            son[x1].push_back(x2);w[x1].push_back(l);
            son[x2].push_back(x1);w[x2].push_back(l);
        }
        res = 0;
        memset(viss,0,sizeof(vis));
        for(i=1;i<=n;i++){
            if(viss[i]==0){
                res = max(res,solve(i));
            }
        }
        printf("%d\n",res);
    }
    return 0;
}
View Code

二、单调队列问题

  就是单调队列+类似尺取法吧

 

下面是完整代码:

#include <cstdio>
#include <iostream>
#include <sstream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;
#define ll long long
#define _cle(m, a) memset(m, a, sizeof(m))
#define repu(i, a, b) for(int i = a; i < b; i++)
#define repd(i, a, b) for(int i = b; i >= a; i--)
#define sfi(n) scanf("%d", &n)
#define sfl(n) scanf("%I64d", &n)
#define pfi(n) printf("%d\n", n)
#define pfl(n) printf("%I64d\n", n)
#define MAXN 1000005

struct Node
{
    int v, len;
    Node* next;
}*head[MAXN], tree[MAXN * 2];
bool vis[MAXN];
int c = 0;
int last[MAXN];
ll maxlen[MAXN], smaxlen[MAXN];
ll dp[MAXN];
ll p[MAXN];
void init()
{
    c = 1;
    _cle(vis, 0);
    _cle(head, NULL);
    _cle(dp, 0);
    _cle(last, 0);
    return ;
}

void Add_Edge(int x, int y, int len)
{
    tree[c].v = y, tree[c].len = len;
    tree[c].next = head[x], head[x] = &tree[c++];
    tree[c].v = x, tree[c].len = len;
    tree[c].next = head[y], head[y] = &tree[c++];
}

void dfs(int fa, int pre)
{
    if(vis[fa]) return ;
    vis[fa] = 1;
    Node* p = head[fa];
    while(p != NULL)
    {
        dfs(p -> v, fa);
        p = p -> next;
    }
    int maxn = 0;
    p = head[fa];
    while(p != NULL)
    {
        if(maxn < p -> len + maxlen[p -> v] && p -> v != pre)
        {
            maxn = p -> len + maxlen[p -> v];
            last[fa] = p -> v;
        }
        p = p -> next;
    }
    maxlen[fa] = maxn;
    maxn = 0;
    p = head[fa];
    while(p != NULL)
    {
        if(p -> v != pre && p -> v != last[fa] && maxn < p -> len + maxlen[p -> v])
            maxn = p -> len + maxlen[p -> v];
        p = p -> next;
    }
    smaxlen[fa] = maxn;
    return ;
}

void DP(int fa)
{
    vis[fa] = 1;
    Node* p = head[fa];
    while(p != NULL)
    {
        if(!vis[p -> v])
        {
            if(last[fa] != p -> v)
                dp[p -> v] = max(dp[fa], maxlen[fa]) + p -> len;
            else
                dp[p -> v] = max(dp[fa], smaxlen[fa]) + p -> len;
            DP(p -> v);
        }
        p = p -> next;
    }
    return ;
}

int main()
{
    int n, m;
    while(sfi(n), sfi(m), n + m)
    {
        init();
        int x, y, len;
        repu(i, 1, n)
        {
            sfi(x), sfi(y), sfi(len);
            Add_Edge(x, y, len);
        }

        dfs(1, -1);
        _cle(vis, 0);
        DP(1);
        repu(i, 1, n + 1) p[i] = max(maxlen[i], dp[i]);
        int Q;
        repu(i, 0, m)
        {
            scanf("%d", &Q);
            ll maxnum = 0;
            ll maxn = p[1];
            ll minn = p[1];
            ll num = 1;
            int last, maxp, minp;
            last = maxp = minp = 1;
            repu(j, 2, n + 1)
            {
                if(p[j] > maxn)
                {
                    if(p[j] - minn > Q)
                    {
                        maxnum = max(maxnum, num);
                        num = 1;
                        j = min(minp + 1, maxp + 1);
                        maxn = minn = p[j];
                        maxp = minp = j;
                    }
                    else num++, maxn = p[j], maxp = j;
                }
                else if(p[j] < minn)
                {
                    if(maxn - p[j] > Q)
                    {
                        maxnum = max(maxnum, num);
                        num = 1;
                        j = min(minp + 1, maxp + 1);
                        maxn = minn = p[j];
                        maxp = minp = j;
                    }
                    else num++, minn = p[j], minp = j;
                }
                else num++;
            }
            maxnum = max(maxnum, num);
            pfl(maxnum);
        }
    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/sunus/p/4712816.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值