线段树与动态规划(例题:poj2374,HDU3016)
来源:互联网 发布:mac删除桌面文件 编辑:程序博客网 时间:2024/06/14 02:47
poj2374
题目大意
每一行有一个栅栏,在(n,s)位置有一群牛要到(0,0)位置去,他们无法翻越栅栏,遇到栅栏只能从两侧绕开,求他们横向移动的最短距离
题目分析
朴素dp:考虑用f[i][0]和f[i][1]分别表示到达i栅栏的左边和右边的时候的最短距离,暴力查找上一个栅栏即可。
线段树优化:由一点点贪心的思想可知,绕路的时候,从一个栅栏的两侧往前走,在没有走到另一个栅栏面前时,可以不横向移动。所以,我们可以从终点往起点处理,线段树维护每一个坐标上上一个栅栏的编号,对于每一个栅栏i,我们更新f[i][0]和f[i][1]的方法是在线段树里查找由i号栅栏两侧往下走会撞到哪个栅栏。接下来更新,比i更加靠近起点的栅栏在a[i]~b[i](i栅栏两侧)这样一段里面往下走都会撞到i栅栏。
不懂的看代码吧,本蒟蒻语死早。
代码
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<climits>using namespace std;const int N=50005;int a[N],b[N],tr[800005],f[N][2];int n,m,lim=100001,ans;void pd(int i){ tr[i<<1]=tr[(i<<1)|1]=tr[i]; tr[i]=0;}int query(int x,int s,int t,int i){//单点查询 if(s==t)return tr[i]; if(tr[i])pd(i); int mid=(s+t)>>1; if(x<=mid)return query(x,s,mid,i<<1); else return query(x,mid+1,t,(i<<1)|1);}void add(int l,int r,int s,int t,int i,int x){//区间修改 if(l<=s&&t<=r){tr[i]=x;return;} if(tr[i])pd(i); int mid=(s+t)>>1; if(l<=mid)add(l,r,s,mid,i<<1,x); if(mid+1<=r)add(l,r,mid+1,t,(i<<1)|1,x);}int main(){ int i,j; scanf("%d%d",&n,&m); a[0]=b[0]=lim;m+=lim; for(i=1;i<=n;++i){ scanf("%d%d",&a[i],&b[i]); a[i]+=lim,b[i]+=lim; j=query(a[i],1,lim<<1,1);//dp值的更新 f[i][0]=min(f[j][0]+abs(a[j]-a[i]),f[j][1]+abs(b[j]-a[i])); j=query(b[i],1,lim<<1,1); f[i][1]=min(f[j][0]+abs(a[j]-b[i]),f[j][1]+abs(b[j]-b[i])); add(a[i],b[i],1,lim<<1,1,i); } ans=min(f[n][0]+abs(a[n]-m),f[n][1]+abs(b[n]-m)); printf("%d\n",ans); return 0;}
HDU3016
题目大意
一个人有100血量,站在最高的板子上想要跳到地面,只能往板子两边跳。每到一块板子i,血量就会增加w[i],如果其血量小于等于0,这个人就死在半路了。求其落到地面的最多血量。
题目分析
将板子按照高度排序后,从最高的板子开始模拟。
线段树维护从每一个坐标跳下“带来”的血量的最大值。
首先对于一块板子i,查找l~r内的最大值x,则这块板子的dp值为x+w[i]。然后,将l~r都变成-inf,代表l~r这一段是不可能跳下来的(因为都被板子i“截住”了),接着,将l和r两个坐标处赋值为x+w[i](前提是x+w[i]>0,这个人没有死掉)。
其余的看代码吧。。。
注意一点:遇到一块dp值为0的板子就立刻输出-1是很错误的行为。。。我。。。我才没有犯过这种错误呢
代码
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<climits>using namespace std;#define LL long longconst int N=100005;int n;LL ans,inf=1e9+7;LL f[N],sum[N<<2],laz[N<<2];struct node{int h,l,r;LL val;}s[N];bool cmp(node x,node y){return x.h>y.h;}void pd(int i){ int l=i<<1,r=(i<<1)|1; if(laz[i]!=0){ sum[l]=laz[i],sum[r]=laz[i]; laz[l]=laz[i],laz[r]=laz[i]; laz[i]=0; }}void chan(int l,int r,int s,int t,int i,LL num){//区间修改 if(l<=s&&t<=r){sum[i]=laz[i]=num;return;} pd(i); int mid=(s+t)>>1; if(l<=mid)chan(l,r,s,mid,i<<1,num); if(mid+1<=r)chan(l,r,mid+1,t,(i<<1)|1,num); sum[i]=max(sum[i<<1],sum[(i<<1)|1]);}LL query(int l,int r,int s,int t,int i){//区间查询 if(l<=s&&t<=r)return sum[i]; pd(i); int mid=(s+t)>>1;LL re=-inf; if(l<=mid)re=query(l,r,s,mid,i<<1); if(mid+1<=r)re=max(re,query(l,r,mid+1,t,(i<<1)|1)); return re;}void build(int s,int t,int i){//初始化 if(s==t){sum[i]=-inf;return;} int mid=(s+t)>>1; build(s,mid,i<<1),build(mid+1,t,(i<<1)|1); sum[i]=max(sum[i<<1],sum[(i<<1)|1]);}int main(){ freopen("lx.in","r",stdin); int i,lim=100000; while(~scanf("%d",&n)){ for(i=1;i<=n;++i)scanf("%d%d%d%lld",&s[i].h,&s[i].l,&s[i].r,&s[i].val); sort(s+1,s+1+n,cmp);ans=0; memset(sum,0,sizeof(sum)); f[1]=100+s[1].val; build(1,lim,1); chan(s[1].l,s[1].l,1,lim,1,f[1]),chan(s[1].r,s[1].r,1,lim,1,f[1]); for(i=2;i<=n;++i){ f[i]=query(s[i].l,s[i].r,1,lim,1)+s[i].val; chan(s[i].l,s[i].r,1,lim,1,-inf); if(f[i]>0){ chan(s[i].l,s[i].l,1,lim,1,f[i]); chan(s[i].r,s[i].r,1,lim,1,f[i]); } } ans=sum[1]; if(ans<0)ans=-1; printf("%lld\n",ans); } return 0;}
阅读全文
0 0
- 线段树与动态规划(例题:poj2374,HDU3016)
- POJ2374 Fence Obstacle Course——线段树+动态规划
- 线段树+DP+hdu3016
- hdu3016 线段树+简单DP
- HDU3016 Man Down(线段树)
- hdu3016 线段树+简单DP
- poj2374 Fence Obstacle Course(线段树+建图spfa)
- 动态规划 与两道例题
- 动态规划经典例题
- 动态规划例题
- 动态规划典型例题
- poj2750 线段树+动态规划
- POJ_1769_动态规划+线段树
- 线段覆盖(动态规划)
- 【算法笔记】动态规划,三个例题(解题思路与C++代码)
- 动态规划算法以及例题
- 动态规划的典型例题
- ACM 动态规划例题详解
- android studio中vector assets的使用说明
- xListViewHeader
- python中的一些函数
- 数据库视频—数据库、表的管理
- HashTable和HashMap的区别详解
- 线段树与动态规划(例题:poj2374,HDU3016)
- 顺序表基本操作(c实现)
- 通过淘宝的ip地址库获取ip地址通用类的实现(C#版)
- Codeforces 853A Planning
- Python中9种生成新对象的方法
- 枚举类
- 正则表达式.*?探究
- xListView主页面请求网络数据展示到listView的三个步骤
- java线程安全总结