观光公交解题思路

解题思路

假设在第i站使用加速器:

1、 假设观光车到达i+1站的时间小于以i+1站为起点的乘客到达i+1站最晚的时间,说明该观光车到达i+1站之后要等待这位乘客到达之后才会出发;若在i站使用加速器,观光车到达i+1站的时间会变短,使在i+1站下车的乘客的花费时间变短,但是观光车到达i+1站之后仍然要等待这位最晚到达的乘客上车之后才能出发,所以这个加速器的最大有效范围只到i+1站。

2、 假设观光车到达i+1的时间等于以i+1站为起点的乘客到达i+1站最晚的时间,说明该观光车到达i+1站之后等无需等待,直接出发;若在i站使用加速器,观光车到达i+1站的时间会变短,使在i+1站下车的乘客的花费时间变短,但是观光车到达i+1站之后仍然要等待这位最晚到达的乘客上车之后才能出发,所以这个加速器的最大有效范围只到i+1站。

3、 假设观光车到达i+1的时间大于以i+1站为起点的乘客到达i+1站最晚的时间,说明观光车到达i+1车站之后无需等待,直接出发;若在i站使用加速器,则加速器的效果会延续到之后的若干站,则在第i站使用加速器的最大有效范围等于在第i+1站使用加速器的最大有效范围。

发现,在i加速,假设影响范围边界在j(i小于j)那么,在i+1~j下车的乘客,都可以获得加速。显然,i以及之前下车的乘客已经毫无关系了。
至于j+1之后的下车的乘客也不可以了,因为到达j之后,加速已经不能影响到后面了。说明,这次加速之后,一定到达j的时间,比j最后一个人到达的时间要晚。也就是说,到达j之后,目的地在j+1之后的乘客,只能继续等待。不论再怎么加速,还是没有用。所以,这一部分也不会节省总时间。
所以,减少的时间就是i+1~j区间下车乘客的人数了。

AC_Code

python
n,m,k = [int(i) for i in  input().split()]
d = [int(i) for i in  input().split()]
N = 1005
tim = [0 for _ in range(N*10)]
st = [0 for _ in range(N*10)]
nd = [0 for _ in range(N*10)]
las = [0 for _ in range(N)]
sum = [0 for _ in range(N)]
go = [0 for _ in range(N)]
rang = [0 for _ in range(N)]
tot = 0
for i in range(m):
    tim[i],st[i],nd[i] = [int(i) for i in input().split()]
    st[i], nd[i] = st[i]-1,nd[i]-1
    las[st[i]] = max(las[st[i]],tim[i])
    sum[nd[i]] +=1
go[0] = 0
for i in range(1,n):
    sum[i]+=sum[i-1]
    go[i]=max(go[i-1],las[i-1])+d[i-1]
for i in range(m):
    #print(go[nd[i]] - tim[i])
    tot += go[nd[i]] - tim[i]
while k:
    k-=1
    rang[n-2] = n-1
    for i in range(n-3,-1,-1):
        if go[i+1]<=las[i+1]:
            rang[i] = i + 1
        else:
            rang[i] = rang[i + 1]
    mx = 0
    id = 0
    for i in range(n-1):
        now = sum[rang[i]]-sum[i]
        if mx<now and d[i]:
            mx = now
            id = i
    if mx == 0:
        break
    tot -= mx

    d[id]-= 1
    for i in range(id,rang[id]+1):
        go[i]=max(go[i-1],las[i-1])+d[i-1]
print(tot)

c++
#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int n,m,k;
int rang[N],d[N],las[N],sum[N];//影响范围,i~i+1长度,i点最后人,前缀和 
int st[10*N],nd[10*N],tim[10*N];//每个人 
int go[N];//到i时间 
int tot;//总时间 
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n-1;i++){
        scanf("%d",&d[i]);
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&tim[i],&st[i],&nd[i]);
        las[st[i]]=max(las[st[i]],tim[i]);
        sum[nd[i]]++;
    }
    go[1]=0;
    for(int i=2;i<=n;i++) sum[i]+=sum[i-1],go[i]=max(go[i-1],las[i-1])+d[i-1];
    for(int i=1;i<=m;i++){
        tot+=go[nd[i]]-tim[i];
    }
    while(k){
        k--;
        rang[n-1]=n;
        for(int i=n-2;i>=1;i--){//更新影响范围 
            if(go[i+1]<=las[i+1]){
                rang[i]=i+1;
            }
            else rang[i]=rang[i+1];
        }
        int mx=0,id=0;
        for(int i=1;i<=n-1;i++){//找最优解 
            int now=sum[rang[i]]-sum[i];
            if(mx<now&&d[i]){
                mx=now,id=i;
            }
        }
        //这里可以特判mx==0 break 但是其实没什么优化 
        tot-=mx;d[id]--;
        for(int i=id+1;i<=rang[i];i++) //最后进行修改,注意循环的范围 
        go[i]=max(go[i-1],las[i-1])+d[i-1];
    }
    printf("%d",tot);
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值