题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=4123

题目大意:

给一棵树,n个节点,每条边有个权值,从每个点i出发有个不经过自己走过的点的最远距离Ma[i],有m个询问,每个询问有个q,求最大的连续节点区间长度ans,使得该区间内最大的M[i]和最小的M[j]之差不超过q。

解题思路一:

这套题目好卡时间。

树形dp+二分+单调队列,几个基本的知识点杂糅在一起。

先用树形dp求出从任意一点i出发的Ma[i].两遍dfs,第一遍求出每个节点为根到儿子方向的最大距离并记录最大距离得到的直接儿子,和与最大距离路径没有重边的次大距离。第二遍求出每个点的最远距离Ma[i]要么从儿子方向得到,要么从父亲方向得到。

然后对于每个询问q,二分区间长度mid,用单调队列求出区间长度为mid的最大Ma[i]和最小Ma[j]的差值的最小值pp。保存起来,当已经求得了该区间长度的值pp时,直接返回pp.

与q比较,因为是存在性问题,每次都把该区间长度的最小的值pp来比较。这样一区间长度为标准,避免了,每次查询相同区间长度只是q不同的情况。不然会超时。

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll __int64
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;#define Maxn 55000int dp[Maxn][2],path[Maxn]; //每个点到儿子节点的最大和次大距离,最大距离经过直接儿子
int Ma[Maxn],q1[Maxn],q2[Maxn],cnt;
int n,m;
int re[Maxn];struct Edge
{int v,len;struct Edge * next;
}*head[Maxn<<1],edge[Maxn<<1];void add(int a,int b,int len)
{++cnt;edge[cnt].v=b,edge[cnt].len=len;edge[cnt].next=head[a];head[a]=&edge[cnt];
}
void dfs1(int cur,int pa) //第一遍dfs,求出最大距离和次大距离以及路径
{struct Edge * p=head[cur];while(p){int v=p->v,len=p->len;if(v!=pa){dfs1(v,cur);if(len+dp[v][0]>dp[cur][0]) //更新最大{dp[cur][1]=dp[cur][0];//更新次大dp[cur][0]=len+dp[v][0];path[cur]=v;}else if(len+dp[v][0]>dp[cur][1]) //更新次大dp[cur][1]=len+dp[v][0];}p=p->next;}
}
void dfs2(int cur,int pa,int tmp)
{Ma[cur]=max(dp[cur][0],tmp);//求出最远距离,要么从儿子方向,要么从父亲方向struct Edge * p=head[cur];while(p){int v=p->v,len=p->len;if(v!=pa){if(v==path[cur])//儿子方向最大值的直接儿子,从父亲中只能选次大dfs2(v,cur,len+max(tmp,dp[cur][1]));elsedfs2(v,cur,len+max(tmp,dp[cur][0]));}p=p->next;}
}
int Cal(int mid)//求区间长度为mid的距离差最大
{if(re[mid]!=-1)return re[mid];int h1=1,t1=0,h2=1,t2=0;int res=INF;for(int i=1;i<=n;i++) //用两个单调队列维护{while(h1<=t1&&Ma[q1[t1]]<=Ma[i])t1--;q1[++t1]=i;while(h1<=t1&&q1[h1]<=i-mid)h1++;while(h2<=t2&&Ma[q2[t2]]>=Ma[i])t2--;q2[++t2]=i;while(h2<=t2&&q2[h2]<=i-mid)h2++;if(i>=mid)res=min(res,Ma[q1[h1]]-Ma[q2[h2]]);}re[mid]=res; //re[i]表示当区间长度为i时,Ma[i]的差值的最小值,作为最优解return res;
}
int main()
{while(scanf("%d%d",&n,&m)&&n+m){cnt=0;memset(head,NULL,sizeof(head));int a,b,c;for(int i=1;i<n;i++){scanf("%d%d%d",&a,&b,&c);add(a,b,c);add(b,a,c);}memset(dp,0,sizeof(dp));memset(re,-1,sizeof(re));dfs1(1,1);//求出以任意点i为根儿子方向的最大和次大距离dfs2(1,1,0);for(int i=1;i<=m;i++){int q;scanf("%d",&q);int l=1,r=n,mid,ans=-1; //二分区间长度while(l<=r){mid=(l+r)>>1;if(Cal(mid)<=q){ans=mid;l=mid+1;}elser=mid-1;}printf("%d\n",ans);}}return 0;
}

解题思路二:

求出Ma[i]数组后,可以用RMQ nlogn的时间复杂度来预处理所有区间的最大值和最小值。

然后对于每个询问q,用两个指针l,r.从前至后按以i开始能够达到最大的区间长度的顺序扫,不过当以i开始的最大的满足的区间长度为L时,向右移动l指针,此时r指针不必移动,因为现在只用考虑区间长度>=L的情况,这样就利用了只找可能满足的区间长度越来越大的情况的性质。这样每个位置最多进出一次,时间复杂度为o(N)。

所以总的时间复杂度为nlogn+m*n.

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll __int64
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;#define Maxn 55000int dp[Maxn][2],path[Maxn]; //每个点到儿子节点的最大和次大距离,最大距离经过直接儿子
int Ma[Maxn],q1[Maxn],q2[Maxn],cnt;
int n,m;struct Edge
{int v,len;struct Edge * next;
}*head[Maxn<<1],edge[Maxn<<1];void add(int a,int b,int len)
{++cnt;edge[cnt].v=b,edge[cnt].len=len;edge[cnt].next=head[a];head[a]=&edge[cnt];
}
void dfs1(int cur,int pa) //第一遍dfs,求出最大距离和次大距离以及路径
{struct Edge * p=head[cur];while(p){int v=p->v,len=p->len;if(v!=pa){dfs1(v,cur);if(len+dp[v][0]>dp[cur][0]) //更新最大{dp[cur][1]=dp[cur][0];//更新次大dp[cur][0]=len+dp[v][0];path[cur]=v;}else if(len+dp[v][0]>dp[cur][1]) //更新次大dp[cur][1]=len+dp[v][0];}p=p->next;}
}
void dfs2(int cur,int pa,int tmp)
{Ma[cur]=max(dp[cur][0],tmp);//求出最远距离,要么从儿子方向,要么从父亲方向struct Edge * p=head[cur];while(p){int v=p->v,len=p->len;if(v!=pa){if(v==path[cur])//儿子方向最大值的直接儿子,从父亲中只能选次大dfs2(v,cur,len+max(tmp,dp[cur][1]));elsedfs2(v,cur,len+max(tmp,dp[cur][0]));}p=p->next;}
}
int rmq1[20][Maxn],rmq2[20][Maxn];
int logg[Maxn];
void rmq_init()
{for(int i=1;i<=n;i++)rmq1[0][i]=rmq2[0][i]=Ma[i];for(int i=1;i<=logg[n];i++) //枚举区间长度,递增{for(int j=1;j<=n;j++){rmq1[i][j]=max(rmq1[i-1][j],rmq1[i-1][j+(1<<(i-1))]);rmq2[i][j]=min(rmq2[i-1][j],rmq2[i-1][j+(1<<(i-1))]);}}
}
int rmq_min(int l,int r)
{int tmp=logg[r-l+1];int a=max(rmq1[tmp][l],rmq1[tmp][r-(1<<tmp)+1]);int b=min(rmq2[tmp][l],rmq2[tmp][r-(1<<tmp)+1]);return a-b;
}
int main()
{logg[0]=-1;for(int i=1;i<=50000;i++)logg[i]=logg[i>>1]+1;  //logg[i]表示int logg2(i)while(scanf("%d%d",&n,&m)&&n+m){cnt=0;memset(head,NULL,sizeof(head));int a,b,c;for(int i=1;i<n;i++){scanf("%d%d%d",&a,&b,&c);add(a,b,c);add(b,a,c);}memset(dp,0,sizeof(dp));dfs1(1,1);//求出以任意点i为根儿子方向的最大和次大距离dfs2(1,1,0);rmq_init();for(int i=1;i<=m;i++){int q;scanf("%d",&q);int l=1,r=1,ans=0;while(r<=n&&l<=n){if(rmq_min(l,r)<=q) //找到以l开始的能满足的最大区间长度{ans=max(ans,r-l+1);r++;}else  //以i+1点开始的区间长度必须大于等于前面的满足区间长度才可以,所以r指针可以不动l++;}printf("%d\n",ans);}}return 0;
}

转载于:https://www.cnblogs.com/james1207/p/3339593.html

两种解法-树形dp+二分+单调队列(或RMQ)-hdu-4123-Bob’s Race相关推荐

  1. HDU 4123 Bob’s Race(树形DP+RMQ)

     题目大意:给定一棵树,每一个点都从当前位置走到距离最远的位置,1~n的连续区间中最大并且走的最远距离差值不超过Q的区间右多大. 思路:首先求出每个点能到达的最远距离,这一步有两种做法: 第一种做 ...

  2. usaco Ordered Fractions 顺序的分数(两种解法)

    这题很好玩,这里有两种解法. 第一种我自己写的,先找到所有的既约真分数,然后写了一个cmp函数进行排序最后输出.我写的时候还在想这章不是搜索吗这跟搜索关系不大吧,难道是怕我们思维定式化故意出的题不是搜 ...

  3. 约瑟夫环问题的两种解法(详解)

    约瑟夫环问题的两种解法(详解) 题目: Josephus有过的故事:39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓.于是决定了自杀方式,41个人排成一个圆 ...

  4. 牛客--追债之旅 两种解法

    文章目录 第一种 第二种: 一共两种解法,所以即便你不会最短路,也可以做,甚至爆搜+剪枝的时间和空间消耗小于最短路做法. 第一种 题意: 小明现在要追讨一笔债务,已知有n座城市,每个城市都有编号,城市 ...

  5. leetcode 73 矩阵置零 C++ 两种解法

    leetcode 73 两种解法~~,没有一个是我想出来的,哈哈~~ one class Solution {public:void setZeroes(vector<vector<int ...

  6. 北林oj-算法设计与分析-Line up in the canteen(两种解法,附思路)

    描述 One day, there is a kind of new delicious food from one of the windows in the canteen. All studen ...

  7. 洛谷——P1597 语句解析(两种解法)

    P1597 语句解析(两种解法) 题目背景 木有背景-- 题目描述 一串长度不超过 255 的 PASCAL 语言代码,只有 a,b,c 3 个变量,而且只有赋值语句,赋值只能是一个一位的数字或一个变 ...

  8. 整数拆分的两种解法(已完成)

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 整数拆分 ...

  9. 动态规划——钢筋切割问题的两种解法解析

    动态规划问题--钢筋切割问题的两种解法解析@TOC 钢筋切割问题: 对于这个问题的两种解法 先来个官方点的解法说明: 我对两种解法的个人理解 第一种解法: 这种解法就是把先钢筋分成两部分,分别记为 i ...

最新文章

  1. JavaScript基础(三)流程控制
  2. 一天搞定CSS:字体font--04
  3. wxWidgets:wxPixelData< Image, PixelFormat >类模板用法
  4. js前台页面显示中文,后台存对应的value值实现
  5. B+/-Tree原理及mysql的索引分析
  6. covid 19如何重塑美国科技公司的工作文化
  7. 如何配置Xshell连接Ubuntu
  8. MySQL 数据库图形化管理界面应用种草之 Navicat Premium 如何使用
  9. python中cmd如何切换盘_redis 中如何切换db
  10. idea加载lombok插件
  11. 基于Vue 和 webpack的项目实现
  12. DOM概述 选取文档元素 1
  13. 一个比较好的多人网络游戏菜单程序。
  14. Jmeter 使用教程
  15. C# ManualResetEvent 类分析
  16. 利用python分析微信聊天记录
  17. c语言中大于号什么意思和作用,程序员必须要使用的大于号,小于号你知道几个?教你轻松入门C语言...
  18. hive 查询表Wrong FS: hdfs://node1/user/hive/warehouse/test1.db, expected: hdfs://node1.zjn.cn:8020/
  19. 三维重建基本流程,搭建一个龙的demo
  20. 根据ID从FASTA文件中批量提取序列【Python】

热门文章

  1. C++库文件导出可见性
  2. 拔掉 MacBookPro,用 8GB 树莓派4 工作一天,体验原来是这样的
  3. 面向对象的C语言编程-DynamicLinkageGenericFunctions--C语言中的偷梁换柱
  4. 问:一行Python代码到底能干多少事情?(一)
  5. 【整理】BIOS、BootLoader、uboot对比
  6. android 用dx.bat 转换class 为dex
  7. Leetcode-最佳买卖股票机含冷冻期
  8. python三引号 内部变量_python在三引号中使用变量
  9. Qt 事件系统的解读
  10. 计算机怎么恢复上一步,电脑怎么还原系统 电脑还原系统步骤盘点