1. 程式人生 > >codeforces 721E. Road to Home(DP+單調佇列)

codeforces 721E. Road to Home(DP+單調佇列)

題目連結http://codeforces.com/contest/721/problem/E
題目大意:在一條座標範圍為[0,L]的路上有n盞路燈,每盞路燈有一個照亮範圍[li,ri]且沒有重疊部分也不相接。某人想在被路燈照到的地方唱歌,他唱一首歌將走過p的距離,一首歌一定要唱完整。在唱完一首歌后,他可以選擇接著唱或者停止,如果停止那麼下一次唱歌的位置必須與停止的位置相隔至少t距離。問他最多能唱多少首歌。
資料範圍:1 ≤ L ≤ 10^9, 0 ≤ n ≤ 100 000, 1 ≤ p ≤ 10^9, 1 ≤ t ≤ 10^9

題解:簡單dp然而愚蠢的我並沒有想到。顯然我們可以知道,如果他在某一段路上唱歌,那麼必然是唱到不能唱為止,即唱到一個位置x滿足x+p>r。於是設f[i]表示到第 i 段為止最多能唱多少首歌,g[i]表示唱完f[i]首歌的最左的端點。得到轉移方程f[i]=f[j]+(r-max(l,g[j]+t))/p,g[i]=r-(r-max(l,g[j]+t))%p。
我們可以知道 f 和g是非遞減的。因為如果i-1的答案比 i 優那麼我們直接用i-1替換i。所以可以更新 i 的 j 應該滿足xi<=j<=yi,其中xi是最靠後的滿足g[xi]+t<=li的點,yi是最靠後的滿足g[yi]+t<=ri的點。由於r[i]< l[i+1],所以g[yi]+t< l[i+1],所以[x[i],y[i]]和[x[i+1],y[i+1]]最多隻有一個交點,那就是yi。這樣更新就是線性的。
時間複雜度O(n)

 

程式碼如下:

#include <algorithm>
#include <cstdio>
int f[100010],g[100010],L,n,p,t;
int main(){
    scanf("%d%d%d%d\n",&L,&n,&p,&t);
    g[0]=-t;
    for (int i=1,j=1;i<=n;i++){
        int l,r;
        scanf("%d%d\n",&l,&r);
        g[i]=g[i-1];f[i]=f[i-1];
        for (j--;j<i && g[j]+t<=r;j++){
            int x=std::max(g[j]+t,l),y=f[j]+(r-x)/p,z=r-(r-x)%p;
            if (y>f[i] || (y==f[i] && z<g[i])) f[i]=y,g[i]=z;
        }
    }
    printf("%d\n",f[n]);
}

--------------------- 本文來自 aufeas 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/aufeas/article/details/53039299?utm_source=copy