题目在上一篇,
本文主要解析贪心 的思路
首先给几个变量的定义
struct NODE
{
long long t;//到达出发点的时刻
int from;//出发点
int to;//目的地
}person[MAXM];
int n,m,k;
int d[MAXN];//i—>i+1需要的时间
long long oversum[MAXN];//在该站点i以前有多少人下车
long long reach_time[MAXN];//车到达站点i的时间
long long latest[MAXN];//在站点i 最晚乘客到达的时间
long long s[MAXN];//如更改d[i]会影响到的站点从[i+1——>s[i]]
那么我们要求的Ans就应该为:所有人到达自身目的地的时间-自己的出发的时间
于是我们可以得到公式:
ans= ∑(reach_time[preson[i].to]-preson[i].t)
=∑(reach_time[preson[i].to])-∑(preson[i].t)
我们可以发现 ∑(preson[i].t) 为定值
所以要使ans达到最小就需要reach_time尽量小
首先我们考虑reach_time怎么更新
若第i-1个站台有人则reach_time=latest[i-1]+d[i-1];
若第i-1个站台无人则reach_time=time[i-1]+d[i-1];
所以当d[i-1]减少(即在这一段路途中使用了加速器)
这样必然导致reach_time的减少
但是我们可以发现:
只有reach_time[i]>laetst[i]时,使用加速器,d[i]的减少才具有意义。
只要reach_time[i]减小了,ans就一定减少
当d[i]减小时它会影响到一段连续的reach_time[i]
所以我们把d[i]减小可影响到的这一段用s[i]来记录
[i+1...s[i]]这一段的reach_time[i]都将减小
我们发现
当reach_time[i]<=latest[i]时 s[i]=i+1;
当reach_time[i]>latest[i]时 s[i]=s[i+1];
所以我们只要每次贪(oversum[s[i]]-oversum[i])最大即可得到最优策略
再把代码上一遍:
#include<cstdio>
#include<cstring>
#include<iostream>
#define MAXN 1000+10
#define MAXM 10000+10
using namespace std;
/*
贪心!!!
*/
struct NODE
{
long long t;
int from;
int to;
}person[MAXM];
int n,m,k;
int d[MAXN];//i—>i+1需要的时间
long long oversum[MAXN];//在该站点i以前有多少人下车
long long reach_time[MAXN];//车到达站点i的时间
long long latest[MAXN];//在站点i 最晚乘客到达的时间
long long s[MAXN];//如更改d[i]会影响到的站点从[i+1——>s[i]]
long long ans;
void init()
{
freopen("bus.in","r",stdin);
freopen("bus.out","w",stdout);
}
void readdate()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<n;i++)
scanf("%d",&d[i]);
for(int i=1;i<=m;i++)
{
scanf("%I64d%d%d",&person[i].t,&person[i].from,&person[i].to);
oversum[person[i].to]++;
latest[person[i].from]=max(latest[person[i].from],person[i].t);
}
return;
}
void pre()//处理最开始的reach_time,s,ans,oversum
{
//当第i-1个站台有乘客需要上车时,reach_time=latest[i-1]+d[i];
//当第i-1个站台无乘客需要上车时,reach_time=reach_time[i-1]+d[i];
//故可以得到公式:reach_time[i]=max(reach_time[i-1],latest[i-1])+d[i-1];
reach_time[1]=0;
for(int i=1;i<=n;i++)
reach_time[i]=max(reach_time[i-1],latest[i-1])+d[i-1];
//若reach_time[i+1]<=latest[i+1]时 s[i]=i+1;
//若reach_time[i+1]>latest[i+1]时 s[i]=s[i+1];
s[n]=n;s[n-1]=n;//初始最后两个站台,进行倒推
for(int i=n-2;i>1;i--)
if(reach_time[i+1]<=latest[i+1])
s[i]=i+1;
else s[i]=s[i+1];
//初始化ans;ans=(第i个人到达目的地的时间-他出发的时间)的总和
for(int i=1;i<=m;i++)
ans+=reach_time[person[i].to]-person[i].t;
//因为读入时oversum不确定,所以在这里处理oversum
for(int i=1;i<=n;i++)
oversum[i]=oversum[i-1]+oversum[i];
return;
}
void solve()
/*
每次找到最大的oversum[s[i]]-oversum[i]
在这时d[i]若减小会得到最优的ans减少
*/
{
long long maxval=0,index;
//找到最优使用加速器的地方
for(int i=1;i<=n;i++)
if(maxval<oversum[s[i]]-oversum[i])
if(d[i]>=1)
{
maxval=oversum[s[i]]-oversum[i];
index=i;
}
long long L=index,R=s[index];
d[index]--;//使用加速器
ans-=maxval;//更改ans
if(R==n) R=n-1;//特判
//再次更新reach_time
for(int i=L;i<=R;i++)
reach_time[i]=max(reach_time[i-1],latest[i-1])+d[i-1];
//再次更新s[i]
for(int i=R;i>=L;i--)
if(reach_time[i+1]<=latest[i+1])
s[i]=i+1;
else s[i]=s[i+1];
return;
}
void work()
{
//对于每一个加速器进行一次贪心
for(int i=1;i<=k;i++)
solve();
printf("%I64d\n",ans);
return;
}
int main()
{
init();
readdate();
pre();
work();
return 0;
}