贪心——Luogu1315 [NOIP2011]观光公交

https://www.luogu.org/problem/show?pid=1315
这是一题贪心题
首先在不放加速器的时候所有乘客的旅行时间总和是一定的。。。
因为每个站的出发时间一定是本站最后一位乘客上车时间(好吧不一定也有可能最后一名到了车还没到。。。)
所以应该是

max(本站最后一位乘客上车时间,车到站时间)

我们需要合理地安排加速器使所有乘客的旅行时间总和最小
那么这个加速器啊就得放得恰到好处
也就是说使这个加速器效果最好,能减少旅行时间最多
于是我们可以找出各站之间的乘坐人数,可以O(n)时间内推出

    for(int i=n;i>1;i--)if(d[i-1]){
        s[i-1]=b[i];
        if(t[i]>a[i])s[i-1]+=s[i];
    }else s[i-1]=0;

其中s[i]表示乘坐人数,b[i]表示在i站下车人数,t[i]表示该站发车最晚时间,d[i]表示相邻两站距离
于是问题就转化成找中间站人数多的对答案贡献大的一段放加速器
那我们就可以直接贪心啦!!!
当然啦这个站放了加速器一定要有效果。。。比如说你不放加速器到站时间是4,然而最后一位乘客上车时间是6,这时你放了加速器就是浪费。。。
这个也是需要考虑到的
最后答案就是在最优加速器方案时所有乘客的旅行时间总和
那么怎么高效的算这个东西呢?
我们一开始输入时做一个预处理:

    for(int i=1;i<=m;i++){
        int tt,aa,bb;
        scanf("%d%d%d",&tt,&aa,&bb);
        b[bb]++;
        ans-=tt;
        a[aa]=max(a[aa],tt);
    }

你可能已经注意到了这个减掉tt的ans,为什么这个ans一开始要减去到站时间呢?
我们接着看最后统计答案时候的代码:

    for(int i=1;i<=n;i++)ans+=t[i]*b[i];

最后这里的t[i]是已经被加速器优化后的最迟到站时间
这里的t[i]*b[i]代表的意义是在i站点下车花费t[i]时间车从1号站出现到抵达本站乘上本站下车人数
(可能“出现”这个词用得不好,但其他我也想不出什么比这个词好的了,原谅我语文差)
这样一来,前后ans抵消掉一部分以后其实就是每位乘客的旅行时间呀
其实就是差分啦~。。。
这个应该很好理解吧
那么这题就被很好地解决了啊
ps:为什么我这个东西速度这么慢。。。人家好像几十毫秒解决了的我总时间用了3000+ms。。。
据说人家用的好像有递推。。。
没事能过就行啦

#include<bits/stdc++.h>
using namespace std;
int d[100001],a[100001],b[100001],t[100001],s[100001],ans=0;
int main()
{
    int n,m,k;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++){
        int tt,aa,bb;
        scanf("%d%d%d",&tt,&aa,&bb);
        b[bb]++;ans-=tt;
        a[aa]=max(a[aa],tt);
    }
    while(k--){
        memset(s,0,sizeof s);
        for(int i=2;i<=n;i++)t[i]=max(t[i-1],a[i-1])+d[i-1];
        for(int i=n;i>1;i--)if(d[i-1]){
            s[i-1]=b[i];
            if(t[i]>a[i])s[i-1]+=s[i];
        }else s[i-1]=0;
        int ma=0,maa=-1;
        for(int i=1;i<n;i++)if(ma<s[i])ma=s[i],maa=i;
        if(maa==-1)break;
        d[maa]--;
    }
    for(int i=2;i<=n;i++)t[i]=max(t[i-1],a[i-1])+d[i-1];
    for(int i=1;i<=n;i++)ans+=t[i]*b[i];
    printf("%d",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值