poj2374 Fence Obstacle Course[线段树+DP]

https://vjudge.net/problem/POJ-2374  


吐槽。在这题上面磕了许久。。英文不好题面读错了qwq,写了个错的算法搞了很久。。A掉之后瞥了一眼众多julao题解,**,怎么想的方法都比我简单,码量还小?太菜了太菜了,把自己烂方法记下来滚了。


题意内容说简单点其实就跟小时候玩的森林冰火人差不多。。看成一个小人从最顶层平台上开始,走到边缘会掉下去,直到落在一个平台或者底部,再继续左或右走。给n层的每层平台起讫点坐标,求x轴方向最小移动距离。

先想到设计状态$f[i][0/1]$表示在第$i$层时($i$从上往下递增),走到最左端或最右端时横向最小移动步数。然后倒序枚举之前每一层,如果可以从其边缘落到现在这层的话,转移之,并且每个线段处理之后覆盖起来,以判断上一层端点是否在覆盖区域内以判断能否掉到这层来。然后发现这个思路优化不了。。$O(n^2logn)$。

换一下思路,看可不可以按dp顺序自顶向下按顺序每dp完一个,就顺带覆盖一次线段。之前可行的端点,可能被覆盖之后就不可行了。所以每次dp完在把当前线段所涵盖的合法端点全清掉,把现在两个端点加入,对于所dp区间内每个横坐标看一下其作为平台端点最晚出现在哪一层,这就是合法决策。

再看转移优化,若合法决策层是$j$,则

$f[i][0]=min { min(f[j][0]+l[j]-l[i],f[j][1]+r[j]-l[i])}$

$f[i][1]=min {min(f[j][0]+r[i]-l[j],f[j][1]+r[i]-r[j])}$。(因为abs很讨厌,所以拆开)

发现其实际只与$j$有关,也就是在线段区间内合法决策中(不合法设为INF)找和$j$相关的最小值,分左右端点讨论转移即可。

也就是用线段树在所在区间上的合法决策去维护这几个域:

$f[i][1]-r[i]$    (再$+r[I]$之后即是从$i$层右端下落后走到第$I$层右端的总距离,下面类推)

$f[i][1]+r[i]$  ($-l[I]$)

$f[i][0]-l[i]$  ($+r[I]$)

$f[i][0]+l[i]$  ($-l[I]$)

进一步发现可以简化成两个域(分类:走到左右)。然后分别将其min取出,和$i$有关的项拼一下完成转移。

然后每次dp完一个线段把他本身两个端点的上述信息加入线段树维护,加入前记得把这段区间所有有效端点去掉,也就是区间改成INF。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define lc i<<1
#define rc i<<1|1
#define dbg(x) cerr<<#x<<" = "<<x<<endl
#define _dbg(x,y) cerr<<#x<<" = "<<x<<"   "<<#y<<" = "<<y<<endl
using namespace std;
typedef long long ll;
template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;}
template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;}
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline T read(T&x){
    x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
    while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
}
const int N=50000+7,M=200000+7,INF=0x3f3f3f3f;
int a[N][2],f[N][2];
int minv0[M<<2],minv1[M<<2],tag[M<<2];
int n,s,Lbound,Rbound,ql,qr;

inline void pushdown(int i){if(tag[i])minv0[lc]=minv0[rc]=minv1[lc]=minv1[rc]=INF,tag[lc]=tag[rc]=1,tag[i]=0;}
void Delete(int i,int L,int R){
    if(ql<=L&&qr>=R){tag[i]=1,minv0[i]=minv1[i]=INF;return;}
    if(tag[i])return;int mid=L+R>>1;
    if(ql<=mid)Delete(lc,L,mid);if(qr>mid)Delete(rc,mid+1,R);
    minv0[i]=_min(minv0[lc],minv0[rc]),minv1[i]=_min(minv1[lc],minv1[rc]);
}
void Update(int i,int L,int R,int k,int p){
    if(L==R){MIN(minv0[i],f[k][p]+a[k][p]),MIN(minv1[i],f[k][p]-a[k][p]);return;}
    pushdown(i);int mid=L+R>>1;
    a[k][p]<=mid?Update(lc,L,mid,k,p):Update(rc,mid+1,R,k,p);
    minv0[i]=_min(minv0[lc],minv0[rc]),minv1[i]=_min(minv1[lc],minv1[rc]);
}
int Query_min0(int i,int L,int R){
    if(ql<=L&&qr>=R)return minv0[i];
    if(tag[i])return INF;int mid=L+R>>1,ret=INF;
    if(ql<=mid)MIN(ret,Query_min0(lc,L,mid));
    if(qr>mid)MIN(ret,Query_min0(rc,mid+1,R));
    return ret;
}
int Query_min1(int i,int L,int R){
    if(ql<=L&&qr>=R)return minv1[i];
    if(tag[i])return INF;int mid=L+R>>1,ret=INF;
    if(ql<=mid)MIN(ret,Query_min1(lc,L,mid));
    if(qr>mid)MIN(ret,Query_min1(rc,mid+1,R));
    return ret;
}
#define all 1,Lbound,Rbound
int main(){//freopen("test.in","r",stdin);//freopen("test.out","w",stdout);
    read(n),a[0][1]=read(a[0][0]);Lbound=Rbound=a[0][0];MIN(Lbound,0),MAX(Rbound,0);
    for(register int i=1;i<=n;++i)MIN(Lbound,read(a[n-i+1][0])),MAX(Rbound,read(a[n-i+1][1]));
    memset(minv0,0x3f,sizeof minv0),memset(minv1,0x3f,sizeof minv1);
    Update(all,0,0),Update(all,0,1);
    for(register int i=1;i<=n;++i){
        f[i][0]=f[i][1]=INF;ql=a[i][0],qr=a[i][1];
        MIN(f[i][0],Query_min0(all)-ql);
        MIN(f[i][1],Query_min1(all)+qr);
        Delete(all),Update(all,i,0),Update(all,i,1);
    }
    int ans=INF;
    ql=Lbound,qr=0;MIN(ans,Query_min1(all));
    ql=0,qr=Rbound;MIN(ans,Query_min0(all));
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/saigyouji-yuyuko/p/10771374.html