HDU 4123 Bob’s Race (树形DP + 单调队列)

博客介绍了如何使用树形动态规划和单调队列解决HDU 4123问题。给定一个带边权的树和若干询问,求解树上节点间距离的最远子序列,使得序列最大值与最小值之差不超过L,文章提供了O(n)复杂度的解决方案。
摘要由CSDN通过智能技术生成

参考

题意:

给定n个点的带边权树Q个询问。

下面n-1行给出树

设dp[i]表示树上离 i 点最远点的距离

下面Q行每行一个数字表示询问。

询问L, 表示求出 dp 数组中最长的连续子序列使得序列中最大值-最小值 <= L,输出这个序列的长度。

思路:

求dp数组就是求个树的直径然后dfs一下。

接着就和这题一样了。

对于每个询问,可以用一个单调队列维护一下。O(n)的回答。

Bob’s Race

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


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

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include <cmath>
using namespace std;
#define ll long long
#define prt(k) ;//cerr<<#k" = "<<k<<endl
const int N = 50050;
const int M = 2 * N;
int n, m;
const ll inf = 0x3f3f3f3f;
int head[M], to[M], next[M], cost[M];
int tot;
void initEdge()
{
    tot = 0;
    memset(head, -1, sizeof head);
}
void addEdge(int u, int v, int w)
{
    to[++tot] = v, cost[tot] = w;
    next[tot] = head[u];
    head[u] = tot;
}
ll dis[N];
int pre[N];
int BFS(int x)  ///return farest point from x
{
    memset(dis, -1, sizeof dis);
    dis[x] = 0;
    pre[x] = -1;
    int far = x;
    queue<int> q;
    q.push(x);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];~i;i=next[i])
        {
            int v = to[i];
            if(dis[v]==-1)
            {
                dis[v] = dis[u] + cost[i];
                pre[v] = u;
                if(dis[far] < dis[v])
                    far = v;
                q.push(v);
            }
        }
    }
    return far;
}
ll dp[N];
bool vis[N];
void dfs(int u)
{
    vis[u] = true;
    for(int i=head[u];~i;i=next[i])
    {
        int v = to[i];
        if(vis[v]) continue;
        dp[v] = dp[u] + cost[i];
        dfs(v);
    }
}
int stk[N];
int len;
void build()    ///预处理树的直径
{
    int E = BFS(1);
    int S = BFS(E);
    int top = 0;
    int u = S;
    len = dis[S];
    memset(vis, 0, sizeof vis);
    while(u != -1)
    {
        stk[top++] = u;
        dp[u] = max(dis[u], len - dis[u]);
        vis[u] = true;
        u = pre[u];
    }
    for(int i=0;i<top;i++)  dfs(stk[i]);
}
int qMax[M], qMin[M];
int main()
{
  //  freopen("in.txt","r",stdin);
    while(scanf("%d%d",&n,&m)==2, n+m)
    {
        initEdge();
        for(int i=0;i<n-1;i++)
        {
            int u,v,w;
            scanf("%d%d%d", &u,&v,&w);
            addEdge(u,v,w);
            addEdge(v,u,w);
        }
        build();
       // for(int i=1;i<=n;i++) printf("dp[%d] = %d\n",i,dp[i]);
        while(m--)
        {
            int v; scanf("%d",&v);
            int ans, hmax, tmax, hmin, tmin;
            ans=hmax=tmax=hmin=tmin=0;
            int st = 0;
            tmax=tmin=-1;
            for(int i=1;i<=n;i++)
            {
                while(hmax<=tmax&&dp[qMax[tmax]]<dp[i]) --tmax;
                qMax[++tmax] = i;
                while(hmin<=tmin&&dp[qMin[tmin]]>dp[i]) --tmin;
                qMin[++tmin] = i;
                while(dp[qMax[hmax]]-dp[qMin[hmin]] > v)
                {
                    if(qMax[hmax]<=qMin[hmin])
                        st = qMax[hmax++];
                    else
                        st = qMin[hmin++];
                }
                ans=max(ans, i-st);
            }
            printf("%d\n", ans);
        }
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值