观光公交[贪心解析]

题目在上一篇,


本文主要解析贪心 的思路

首先给几个变量的定义

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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值