当前位置: 代码迷 >> 综合 >> 观光公交[NOIP2011-Day2T3][贪心]
  详细解决方案

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

热度:95   发布时间:2023-11-19 10:14:28.0

文章目录

  • 题目
  • 思路
  • 代码
  • 思考

题目

Luogu
1≤n≤1,000,1≤m≤10,000,0≤k≤100,000,0≤Di?≤1000,0≤Ti≤100,0001≤n≤1,000,1≤m≤10,000,0≤k≤100,000,0≤D_i?≤1000,0≤T_i≤100,0001n1,000,1m10,000,0k100,000,0Di??1000,0Ti?100,000

思路

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

代码

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

思考

多积累对贪心题的认知