NOIP2011 观光公交(贪心)

好吧,看到这道题的题目就晕了尴尬,好长一串。。。

由于找不出来什么特别NB的算法,就上贪心吧。。。

网上看了下各种题解,对于某个车站i,如果汽车的到达i站的时间比最后一个来到第i站的时间要短的话,我们就可以考虑使用加速器了。因此枚举在哪个车站i使用加速器能够使节约的时间最多,每次使用了加速器后,更新一遍汽车到站的时间。一直不停地找最优的车站使用加速器,直到加速器用光或者用不用加速器都一样的时候就退出循环。

此种写法是一个一个地使用加速器,比较慢,但还是能过完所有点。

#include<cstdio>
#include<iostream>
#define MAXN 1005
using namespace std;
int n,m,k;
int d[MAXN],down[MAXN],last[MAXN];//到达第i+1站时间,每站下车人数,最后一个到达第i站的人
int t[10005],fr[10005],to[10005];//每个人的到站时间,起始站,终点站
int arrive[MAXN];//汽车到达第i站的时间
int main()
{
    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("%d%d%d",&t[i],&fr[i],&to[i]);
        last[fr[i]] = max(last[fr[i]],t[i]);//统计到每个站的最后一个人
        down[to[i]]++;//统计每站下车人数
    }
    arrive[1] = last[1];
    while(k)
    {
        for(int i = 2; i <= n; i++) arrive[i] = max(arrive[i-1],last[i-1]) + d[i-1];//由于k在变化,更新汽车到达第i站的时间
        int pos = 0,maxpeople = 0;//使用加速器的站点,该点使用加速器能作用的最大人数
        for(int i = 1; i < n; i++)//枚举使用加速器的站点
        if(d[i] > 0)
        {
            int temp = 0;
            for(int j = i+1; j <= n; j++)
            {
                temp += down[j];
                if(arrive[j] <= last[j]) break;//如果汽车到达j点时,还有乘客没有来或者刚来,使用加速器是对后面没有影响的
            }
            if(temp > maxpeople)
            {
                maxpeople = temp;
                pos = i;
            }
        }
        d[pos]--;
        k--;
    }
    for(int i = 2; i <= n; i++)//更新使用了最后一个加速器后的情况
        arrive[i] = max(arrive[i-1],last[i-1]) + d[i-1];
    int ans = 0;
    for(int i = 1; i <= m; i++)
        ans += arrive[to[i]] - t[i];
    printf("%d\n",ans);
}


这种写法是通过计算得到在最优的车站i使用多少个加速器能达到的最优解,由于是一堆一堆地使用加速器,比起上面那种写法要快得多。

#include<cstdio>
#include<iostream>
#define MAXN 1005
using namespace std;
int n,m,k;
int d[MAXN],down[MAXN],last[MAXN];//到达第i+1站时间,每站下车人数,最后一个到达第i站的人
int t[10005],fr[10005],to[10005];//每个人的到站时间,起始站,终点站
int sum[MAXN],arrive[MAXN],r[MAXN];//汽车到达第i站的时间,在第i站用加速器能影响到的最大站
int main()
{
    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("%d%d%d",&t[i],&fr[i],&to[i]);
        last[fr[i]] = max(last[fr[i]],t[i]);//统计到每个站的最后一个人
        down[to[i]]++;//统计每站下车人数
    }
    for(int i = 2; i <= n; i++)
        arrive[i] = max(arrive[i-1],last[i-1]) + d[i-1];//统计汽车到站的时间
    for(int i = 1; i <= n; i++)//统计在前i站下车的前缀和
        sum[i] = sum[i-1] + down[i];
    int R = 1;
    for(int i = 1; i < n; i++)//计算在第i站使用加速器的影响范围
    {
        while((R < n&&last[R] < arrive[R])||R <= i) R++;
        r[i] = R;
    }
    while(k)
    {
        int maxpeople = 0,pos = 0,maxtime = 123456789,usek;
        for(int i = 1; i < n; i++)//找出使用加速器影响最大的人数
        if(sum[r[i]] - sum[i] > maxpeople&&d[i] > 0)
        {
            maxpeople = sum[r[i]] - sum[i];
            pos = i;
        }
          
        if(maxpeople == 0) break;//已经没有能影响的人了,所以再使用加速器也没用
          
        for(int i = pos+1; i < n&&last[i] < arrive[i]; i++)//使用加速器的个数应该不超过 last[i]-arrive[i]的最小值,如果超过了,那么汽车到达i的时间就会晚于最后一个到达i的人,达不到最优解
            maxtime = min(maxtime,arrive[i] - last[i]);
        maxtime = min(maxtime,k);//使用加速器的限制条件
        usek = min(maxtime,d[pos]);
        k -= usek;
        d[pos] -= usek;
          
        for(int i = pos+1; i <= r[pos]; i++)//更新汽车到站的时间
            arrive[i] = max(arrive[i-1],last[i-1])+d[i-1];
        for(int i = pos,R = pos; i < r[pos]; i++)//更新使用加速器的影响范围
        {
            while((R < n&&last[R] < arrive[R])||R <= i) R++;
                if(R >= r[i]) break;//影响范围不可能比没用的时候还大
            r[i] = R;
        }
    }
    long long ans = 0;
    for(int i = 1; i <= m; i++)
        ans += (long long)arrive[to[i]] - t[i];
    printf("%lld\n",ans);
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值