1. 程式人生 > >NOIP2011day2 觀光公交(堆+貪心)

NOIP2011day2 觀光公交(堆+貪心)

題意

n 個景點, m 個遊客,第 i

個遊客在 T i 時間到達 A i 景點,要去
i" role="presentation" style="position: relative;"> B i 景點。有一個公交車在 0 時刻在 1 號景點出現,從 i 號景點到 i + 1 號景點需要 D i 時間,從第 i 個景點去往第 i + 1 個景點必須保證所有出現在 A i 的人均上車(上下車不需時間)。現在有 k 個加速器,每個加速器可以使得某一個 D i 1 ,但不能小於 0 ,求所有遊客從剛到景點到下車時間差值的和最小是多少。
1 n 1000
1 m 10000

思路

題目比較複雜,但所求還是比較清晰的,因為遊客剛到景點的時間已經給定,所以只用求所有遊客下車時間總和的最小值即可。我們單獨分析在每個點使用加速器的效果,不難發現,當公交車不用等來的晚的遊客時,這個效果可以抑制持續下去,直到遇到了一個來的和公交車一樣晚,甚至更晚的遊客,在這之後下車的遊客便不受影響了。這樣我們也不難發現一個貪心決策,先處理處一下連續影響的區間,按照下車人數從大到小排序,每彈出一個 [ l , r ] 決策是在 l 1 l 的路上用加速器,直到某一個點的人來的時間和公交車一樣了,並把這個區間沿著這個點劈開;而當 D l 1 被加速成 0 時還沒有遇到劈開,那就把 [ l + 1 , r ] 加入堆。

程式碼

#include<bits/stdc++.h>
#define FOR(i,x,y) for(register int i=(x);i<=(y);++i)
#define DOR(i,x,y) for(register int i=(x);i>=(y);--i)
#define N 1003
#define M 10003
typedef long long LL;
using namespace std; //S[i]表示目前公交車剛到i的時間 
int onbus[N],cnt[N],D[N],S[N],T[M],A[M],B[M];
int n,m,K;
struct node
{
    int l,r;  //使[l,r]中的乘客時間減少1 
    bool operator <(const node &_)const{return cnt[r]-cnt[l-1]<cnt[_.r]-cnt[_.l-1];}
};
priority_queue<node>q;

int main()
{
    scanf("%d%d%d",&n,&m,&K);
    FOR(i,1,n-1)scanf("%d",&D[i]);
    FOR(i,1,m)
    {
        scanf("%d%d%d",&T[i],&A[i],&B[i]);
        onbus[A[i]]=max(onbus[A[i]],T[i]);
        cnt[B[i]]++;
    }
    FOR(i,2,n)cnt[i]+=cnt[i-1];
    FOR(i,2,n)S[i]=max(S[i-1],onbus[i-1])+D[i-1];
    FOR(i,2,n)
        if(S[i]>onbus[i])
        {
            FOR(j,i,n)
                if(j==n||S[j]<=onbus[j])
                {
                    q.push((node){i,j});
                    i=j;
                    break;
                }
        }
    while(!q.empty()&&K>0)
    {
        node now=q.top();q.pop();
        int miner=min(K,D[now.l-1]),p=-1;
        FOR(i,now.l,now.r-1)
            if(S[i]-onbus[i]<miner)
            {
                miner=S[i]-onbus[i];
                p=i;
            }
        D[now.l-1]-=miner,K-=miner;
        FOR(i,now.l,now.r)S[i]=max(S[i-1],onbus[i-1])+D[i-1];
        if(~p)q.push((node){now.l,p}),q.push((node){p+1,now.r});
        else if(now.l<now.r)q.push((node){now.l+1,now.r});  //無法繼續加速 
    }
    int ans=0;
    FOR(i,1,m)ans+=S[B[i]]-T[i];
    printf("%d\n",ans);
    return 0;
}