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