POJ - 3162 Walking Race 树形dp 单调队列

POJ - 3162Walking Race

  题目大意:有n个训练点,第i天就选择第i个训练点为起点跑到最远距离的点,然后连续的几天里如果最远距离的最大值和最小值的差距不超过m就可以作为观测区间,问这样的区间最长的长度?

  一开始楞是没看懂题意,最讨厌这种四级题,这是在刁难我英语小能手(能用翻译的就不自己动手)。而且这题感觉单调队列那里的处理更难一点,不过还是来说一说怎么树形dp取得最远距离,先画个简简单单丑丑的图

  我们直接从1作为根节点开始dfs的话,可以处理1的最远距离,并且可以得出到其它节点在其子树中的最短距离,然后怎么得到其他节点的最远距离呢?我们还是从1开始,2的最短距离无非就是1和2连接这一条边再加上除这条外的最长分支也就是1和4这条边,而3的话1和3连接的这条边是在1和4这条路径上的,所以对3来说最远距离就是1的除1和4这条外其他最长分支加上1和3连接的这条边与3和4连接的这条边中的最大值,所以我们可以更新一个每个节点的最大距离以及第二远距离还有最远距离来自哪个子节点的方向。

  这样的话先一遍dfs得出作为根节点的情况,还有其他节点到子树的情况,然后第二边dfs再由根节点去传入数据更新其他节点的最大值,也就判断子节点是否是最远距离方向的,是的话传入第二远距离,否则传入最远距离。

  然后就是取区间那里了,首先尺取思想,但需要一个单调递减队列维护区间最大值(最大值在队列首部),然后还需要一个单调递增队列维护区间最小值(最小值在队列首部),然后区间往右的同时,更新左端,详情见代码。

  1 #include<cstdio>
  2 #include<queue>
  3 #include<algorithm>
  4 using namespace std;
  5 const int N=1000118;
  6 struct Side{
  7     int v,ne,len;
  8 }S[2*N];
  9 int sn,n,m,head[N],dis1[N],dis2[N],path[N];
 10 void add(int u,int v,int c)
 11 {
 12     S[sn].v=v;
 13     S[sn].len=c;
 14     S[sn].ne=head[u];
 15     head[u]=sn++;
 16 }
 17 void updata(int u,int v,int dis)//更新距离
 18 {
 19     if(dis>dis1[u])//更新最大距离的来源点
 20         path[u]=v;
 21     dis2[u]=max(dis2[u],min(dis1[u],dis));
 22     dis1[u]=max(dis1[u],dis);
 23 }
 24 void dfs1(int u,int f)
 25 {
 26     for(int i=head[u];i!=-1;i=S[i].ne)
 27     {
 28         int v=S[i].v;
 29         if(v!=f)
 30         {
 31             dfs1(v,u);//先搜索子节点
 32             updata(u,v,dis1[v]+S[i].len);//更新
 33         }
 34     }
 35 }
 36 void dfs2(int u,int f,int len)
 37 {
 38     updata(u,f,len);//由父节点传入的距离更新距离
 39     for(int i=head[u];i!=-1;i=S[i].ne)
 40     {
 41         int v=S[i].v;
 42         if(v!=f)
 43         {
 44             if(v==path[u])
 45                 dfs2(v,u,dis2[u]+S[i].len);
 46             else
 47                 dfs2(v,u,dis1[u]+S[i].len);
 48         }
 49     }
 50 }
 51 int main()
 52 {
 53     int f,d;
 54     while(~scanf("%d%d",&n,&m))
 55     {
 56         for(int i=0;i<=n;i++)
 57         {
 58             head[i]=-1;
 59             dis1[i]=dis2[i]=0;
 60         }
 61         sn=0;
 62         for(int i=1;i<n;i++)
 63         {
 64             scanf("%d%d",&f,&d);
 65             add(i+1,f,d);
 66             add(f,i+1,d);
 67         }
 68         dfs1(1,0);
 69         dfs2(1,0,0);
 70         deque<int> qx,qd;//qx单调递增队列,qd单调递减队列
 71         int ans=0;         //注意是双向队列,单向没有back()
 72         for(int i=1,j=0;i<=n;i++)//j是左端,i是右端
 73         {
 74             //维护单调性
 75             while(qd.size()&&dis1[qd.back()]<dis1[i])
 76                 qd.pop_back();
 77             while(qx.size()&&dis1[qx.back()]>dis1[i])
 78                 qx.pop_back();
 79             qd.push_back(i);
 80             qx.push_back(i);
 81             while(qd.size()&&qx.size()&&dis1[qd.front()]-dis1[qx.front()]>m)
 82             {
 83                 //当最大值减最小值大于m时这时左端的位置就需要调整了
 84                 //左端肯定是要调整到最大值和最小值中较近的位置的前面
 85                 if(qd.front()<qx.front())
 86                 {
 87                     j=qd.front();
 88                     qd.pop_front();
 89                 }
 90                 else
 91                 {
 92                     j=qx.front();
 93                     qx.pop_front();
 94                 }
 95                 //把区间外的数全部弹出
 96                 while(qd.size()&&qd.front()<=j)
 97                     qd.pop_front();
 98                 while(qx.size()&&qd.front()<=j)
 99                     qx.pop_front();
100             }
101             ans=max(ans,i-j);//更新区间长度
102         }
103         printf("%d\n",ans);
104     }
105     return 0;
106 }

简简单单的低调

  连做了几题树形dp,不怎么说得出,不过感觉还是有点心得的。核心的思想还是在于它的一棵数的结构,我们可以把某个节点假设为根节点再由它的情况去推导出整棵树的情况。而且精华部分也正如dp,搞清楚dp什么,然后是状态转移的过程。想练好它,无疑就两个方法,做题,思考。

原文地址:https://www.cnblogs.com/LMCC1108/p/10534663.html

时间: 2024-10-23 00:56:57

POJ - 3162 Walking Race 树形dp 单调队列的相关文章

POJ 3162 Walking Race 树形DP+线段树

给出一棵树,编号为1~n,给出数m 漂亮mm连续n天锻炼身体,每天会以节点i为起点,走到离i最远距离的节点 走了n天之后,mm想到知道自己这n天的锻炼效果 于是mm把这n天每一天走的距离记录在一起,成为一段长度为n的数列 现在mm想要从这数列中选出一个连续的区间,要求这个区间的max-min<=m 输出最长的区间 做了一个下午 思路: 分成2个部分: 1.求出数列,即对于一棵树,求出每一个节点能到达的最远距离 2.对于这段数列,选出一个区间,使得区间的max-min<=m,并且使得区间长度尽量

POJ 3162 Walking Race 树形dp 优先队列

http://poj.org/problem?id=3162 题意 :  一棵n个节点的树.wc爱跑步,跑n天,第i天从第i个节点开始跑步,每次跑到距第i个节点最远的那个节点(产生了n个距离),现在要在这n个距离里取连续的若干天,使得这些天里最大距离和最小距离的差小于M,问怎么取使得天数最多? 每个点的最大距离和之前http://acm.hdu.edu.cn/showproblem.php?pid=2196这道题一样 (就是求每个子树的最长子链,次长子链,然后求经过父亲节点能达到的最大值,比较一

[POJ3162]Walking Race(DP + 单调队列)

传送门 题意:一棵n个节点的树.wc爱跑步,跑n天,第i天从第i个节点开始跑步,每次跑到距第i个节点最远的那个节点(产生了n个距离),现在要在这n个距离里取连续的若干天,使得这些天里最大距离和最小距离的差小于M,问怎么取使得天数最多? 求每个点到最远距离的点的距离可以用 computer 的方法. 至于第二问,可以跑一遍2个单调队列. 具体是固定左端点,右端点向右平移到最远,直到不能平移,再左端点向右平移一位.在这中间维护单调队列和更新 ans 最大值. 具体细节看代码 ——代码 1 #incl

POJ 3162 Walking Race (树的直径,单调队列)

题意:给定一棵带边权的n个节点的树,首先要求出每个点的最长路,然后写成序列d[1],d[2]...d[n],然后求满足 区间最大值-区间最小值<=k 的最大区间长度为多少? 思路: 分两步进行: (1)最多仅需3次DFS就可以在O(n)时间内求出每个点的最长路了.先从任意点t出发遍历过有点,记录下从点t出发到每个点的最长路,然后从记录的其中一个距t最远的点root出发,再一次DFS,就可以得到离root最远的点e啦,再从e出发DFS一次,就得到所有点的最长路了.注意3次DFS的代码都是一样的喔~

POJ 3162 Walking Race(树的直径+单调队列)

题目大意:对一棵树,求出从每个结点出发能到走的最长距离(每个结点最多只能经过一次),将这些距离按排成一个数组得到dis[1],dis[2],dis[3]……dis[n] ,在数列的dis中求一个最长的区间,使得区间中的最大值与最小值的差不超过m. 思路:先找出树的直径的两个端点来,那么树当中的其它节点能走的最大距离一定是到这个两个点当中的其中一个的.所以三遍bfs就可以求出来这个最远的距离,那么,这个最远的距离求出来之后再用两个单调队列来维护最大值和最小值. /*****************

bzoj2500: 幸福的道路(树形dp+单调队列)

好题.. 先找出每个节点的树上最长路 由树形DP完成 节点x,设其最长路的子节点为y 对于y的最长路,有向上和向下两种情况: down:y向子节点的最长路g[y][0] up:x的次长路的g[x][1]+dis[x][y] up:up[fa[x]]+dis[x][y] dfs1找向下,即向子节点的最长路 dfs2找向上的最长路 最后最长路f[i]=max(up[x],g[x][0]) 第二部分 找最长连续子序列,使得序列中abs(mx-mn)<=m 这次学习了用单调队列的做法 两个队列mx,mn

BZOJ 2500 幸福的道路 树形DP+单调队列

题目大意:给定一棵树,令a[i]为从第i个节点出发的最长链,求a[i]中最长的区间,满足区间内最大值与最小值之差不超过m 读错题害死人,脑残害死人 求a[i]显然是树形DP 考虑从一个点出发的链可以从子节点走,也可以从父节点走 因此我们DP两次,第一次求出从子节点走的最长链,第二次求出从父节点走的最长链,两次取max就是答案 但是直接DP会有问题,因为从父节点走的最长链可能是从自己的子树出发的,这样就会走重 因此除记录从子节点出发的最长链外还要记录一个从另一个子节点出发的次长链,如果最长链长度相

bzoj2500幸福的道路 树形dp+单调队列

2500: 幸福的道路 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 434  Solved: 170[Submit][Status][Discuss] Description 小T与小L终于决定走在一起,他们不想浪费在一起的每一分每一秒,所以他们决定每天早上一同晨练来享受在一起的时光. 他们画出了晨练路线的草图,眼尖的小T发现可以用树来描绘这个草图. 他们不愿枯燥的每天从同一个地方开始他们的锻炼,所以他们准备给起点标号后顺序地从每个起点开始(第

hdu 4123 树形DP+单调队列

http://acm.hust.edu.cn/vjudge/problem/25790 这题基本同poj 3162 要注意mx,mx2,vx,vx2每次都要初始化 #include <iostream> #include <string> #include <cstring> #include <cstdlib> #include <cstdio> #include <cmath> #include <algorithm>