1. 程式人生 > >poj2374 Fence Obstacle Course(DP)(線段樹)

poj2374 Fence Obstacle Course(DP)(線段樹)

題目

題解

線段樹優化DP 設f[i][0/1]表示在通過第i條柵欄後,處於柵欄左邊/右邊的最小路徑長。 因為奶牛是直線下來的,所以最優方案當然是從上一個柵欄的這個位置下來。由於有柵欄的影響,奶牛們不能順利的下來,此時到達這個位置的最優策略要麼是從前面那個柵欄的左端點過來,要麼從右端點過來。所以有f[i][0]=\min(f[j][0]+abs(l[i]-l[j]),f[j][1]+abs(l[i]-r[j])f[i][1]=\min(f[j][0]+abs(r[i]-l[j]),f[j][1]+abs(r[i]-r[j]))。 其中的j就是上一個擋住了這個位置的柵欄。我們可以用線段樹來維護這個柵欄的編號。當柵欄(l[i],r[i]),出現後,我們把線段樹上(l[i],r[i])這段區間改成i,表示這個位置是柵欄i阻擋了。對於後面的柵欄,修改時直接覆蓋前面的資訊即可。我們只要實現一個改段求點的線段樹即可。 特別的,線段樹初始值為0。一個位置如果得到的j=0,那麼說明它前面沒有柵欄,它可以直接從s過來,路徑=abs(s-p)。

程式碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#define rt 1,1,tot
using namespace std;
const int maxn=50010,maxtr=maxn*2*4;

int hh[maxtr],la[maxtr];

void update(int x)
{
    if(la[x]==-1) return ;
    int lc=x<<1,rc=lc|1;
    hh[lc]=hh[rc]=la[x];
    la[lc]=la[rc]=la[x];
    la[x]=-1;
}

void change(int x,int xl,int xr,int l,int r,int c)
{
    if(xl==l && xr==r)
    {
        hh[x]=la[x]=c;
        return ;
    }
    int mid=xl+xr>>1;
    int lc=x<<1,rc=lc|1;
    update(x);
    if(r<=mid) change(lc,xl,mid,l,r,c);
    else if(mid<l) change(rc,mid+1,xr,l,r,c);
    else change(lc,xl,mid,l,mid,c),change(rc,mid+1,xr,mid+1,r,c);
}

int ask(int x,int xl,int xr,int p)
{
    if(xl==xr)
    {
        return hh[x];
    }
    int mid=xl+xr>>1;
    int lc=x<<1,rc=lc|1;
    update(x);
    if(p<=mid) return ask(lc,xl,mid,p);
    else return ask(rc,mid+1,xr,p);
}

int l[maxn],r[maxn];
int a[maxn*2];int tot=0;
int ul[maxn],ur[maxn];
int f[maxn][2];

int main()
{
    int n,s;
    scanf("%d%d",&n,&s);
    for(int i=n;i>=1;i--)
    {
        scanf("%d%d",&l[i],&r[i]);
        a[++tot]=l[i];a[++tot]=r[i];
    }
    a[++tot]=0;a[++tot]=s;
    sort(a+1,a+tot+1);tot=unique(a+1,a+tot+1)-(a+1);
    for(int i=1;i<=n;i++) ul[i]=lower_bound(a+1,a+tot+1,l[i])-a,ur[i]=lower_bound(a+1,a+tot+1,r[i])-a;
    
    for(int i=1;i<=n;i++)
    {
        int fl=ask(rt,ul[i]),fr=ask(rt,ur[i]);
        if(fl==0) f[i][0]=abs(s-l[i]);
        else f[i][0]=min(f[fl][0]+abs(l[fl]-l[i]),f[fl][1]+abs(r[fl]-l[i]));
        if(fr==0) f[i][1]=abs(s-r[i]);
        else f[i][1]=min(f[fr][0]+abs(l[fr]-r[i]),f[fr][1]+abs(r[fr]-r[i]));
        change(rt,ul[i],ur[i],i);
    }
    int e=lower_bound(a+1,a+tot+1,0)-a;
    int ff=ask(rt,e);
    if(ff==0) printf("%d\n",abs(s));
    else printf("%d\n",min(f[ff][0]+abs(l[ff]),f[ff][1]+abs(r[ff])));
    return 0;
}