线段树与动态规划(例题: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;}
原创粉丝点击