观光公交[NOIP2011-Day2T3][贪心]

题目

Luogu
1 ≤ n ≤ 1 , 000 , 1 ≤ m ≤ 10 , 000 , 0 ≤ k ≤ 100 , 000 , 0 ≤ D i ​ ≤ 1000 , 0 ≤ T i ≤ 100 , 000 1≤n≤1,000,1≤m≤10,000,0≤k≤100,000,0≤D_i​≤1000,0≤T_i≤100,000 1n1,000,1m10,000,0k100,000,0Di1000,0Ti100,000

思路

首先思考,问什么可以贪心?
据说当初考试时,很多人想的 D p Dp Dp 然后就挂了
这也是出题人和考生玩心理战术
为了更好说明为什么可以贪心,我们记
f i f_i fi 为不使用氮气加速到达 i i i 点时间,
c n t i cnt_i cnti i i i 点下车人数,
L a s t i Last_i Lasti i i i 点最晚出发时间
f i = m a x { f i − 1 , L s t i − 1 } + d i − 1 f_i=max\{f_{i-1},Lst_{i-1}\}+d_{i-1} fi=max{fi1,Lsti1}+di1
对发现我们只对 f i f_i fi, L a s t i Last_i Lasti 的大小关系会影响我们决策
也就是人等车和车等人两种
我们可以进行分类讨论
1. f i > L a s t i f_i>Last_i fi>Lasti(人等车)
由于它加速后也会影响到后一段,所以可以暴力找出在它后面连续的一段 f j > L a s t j f_j>Last_j fj>Lastj
在这些位置下车的人都会提前 1 1 1 单位时间下车
2. f i < = L a s t i f_i<=Last_i fi<=Lasti(车等人)
此时并不会对 i i i 后面的产生影响,只会影响这 c n t i cnt_i cnti 个人
综上我们每次只需要这样一段就行了:
在这里插入图片描述
影响的人数是这一段下车人数之和
问题来了,为什么可以贪心?
口胡一下(因为贪心本身就不好说)
因为每一段有 D i D_i Di 的限制
假设现在你就取包含这一段最大的区间 D i D_i Di 又恰好是最小的
你早取晚取都会把 D i D_i Di 取完,然后就裂成了两段
假设不取 D i D_i Di 没有取 D i D_i Di
每次取最大的影响人数
觉得应该可以从 D p Dp Dp 决策最优来讲,但是懒,不会

代码

#include<map>
#include<set>
#include<stack>
#include<queue>
#include<vector>
#include<ctime>
#include<bitset>
#include<cmath>
#include<cstdio>
#include<climits>
#include<cstdlib>
#include<cstring>
#include<sstream>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
#define ULL unsigned long long
int read(){
	bool f=0;int x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c=='-')f=1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return !f?x:-x;
}
#define MAXN 100000
#define INF 0x3f3f3f3f
int d[MAXN+5],Last[MAXN+5],cnt[MAXN+5],f[MAXN+5];
int Max(int a,int b){return a>b?a:b;}
int T[MAXN+5],A[MAXN+5],B[MAXN+5];
int main(){//f[i]:到达 i 实际时间 
	int n=read(),m=read(),k=read();
	for(int i=1;i<n;i++)
		d[i]=read();
	for(int i=1;i<=m;i++){
		T[i]=read(),A[i]=read(),B[i]=read();
		Last[A[i]]=Max(Last[A[i]],T[i]);
		cnt[B[i]]++;
	}
	f[1]=0;
	for(int i=2;i<=n;i++)
		f[i]=Max(f[i-1],Last[i-1])+d[i-1];
	while(k--){
		int Maxnum=0,L=0,R=0;
		for(int i=2;i<=n;i++){
			if(d[i-1]==0) continue;
			int tmp=0,p1=i,p2=n;
			for(int j=i;j<=n;j++){
				tmp+=cnt[j];
				if(f[j]<=Last[j]){
					p2=j;
					break;
				}
			}
			if(tmp>Maxnum)
				Maxnum=tmp,L=p1,R=p2;
		}
		if(!Maxnum) break;
		d[L-1]--;
		for(int i=L;i<=R;i++)
			f[i]--;
	}
	int ans=0;
	for(int i=1;i<=m;i++)
		ans+=f[B[i]]-T[i];
	printf("%d\n",ans);
    return 0;
}


思考

多积累对贪心题的认知

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值