【z06】观光公交

描述
风景迷人的小城Y市,拥有n个美丽的景点。由于慕名而来的游客越来越多,Y市特意安排了一辆观光公交车,为游客提供更便捷的交通服务。观光公交车在第0分钟出现在1号景点,随后依次前往2、3、4……n号景点。从第i号景点开到第i+1号景点需要Di分钟。任意时刻,公交车只能往前开,或在景点处等待。
设共有m个游客,每位游客需要乘车1次从一个景点到达另一个景点,第i位游客在Ti分钟来到景点Ai,希望乘车前往景点Bi(Ai< Bi)。为了使所有乘客都能顺利到达目的地,公交车在每站都必须等待需要从该景点出发的所有乘客都上车后才能出发开往下一景点。假设乘客上下车不需要时间。
一个乘客的旅行时间,等于他到达目的地的时刻减去他来到出发地的时刻。因为只有一辆观光车,有时候还要停下来等其他乘客,乘客们纷纷抱怨旅行时间太长了。于是聪明的司机ZZ给公交车安装了k个氮气加速器,每使用一个加速器,可以使其中一个Di减1。对于同一个Di可以重复使用加速器,但是必须保证使用后Di大于等于0。
那么ZZ该如何安排使用加速器,才能使所有乘客的旅行时间总和最小?
格式
输入格式

第1行是3个整数n, m, k,每两个整数之间用一个空格隔开。分别表示景点数、乘客数和氮气加速器个数。
第2行是n-1个整数,每两个整数之间用一个空格隔开,第i个数表示从第i个景点开往第i+1个景点所需要的时间,即Di。
第3行至m+2行每行3个整数Ti, Ai, Bi,每两个整数之间用一个空格隔开。第i+2行表示第i位乘客来到出发景点的时刻,出发的景点编号和到达的景点编号。
输出格式

共一行,包含一个整数,表示最小的总旅行时间。
样例1
样例输入1[复制]

3 3 2
1 4
0 1 3
1 1 2
5 2 3
样例输出1[复制]

10
限制
1s
提示
样例说明:
对D2使用2个加速器,从2号景点到3号景点时间变为2分钟。
公交车在第1分钟从1号景点出发,第2分钟到达2号景点,第5分钟从2号景点出发,第7分钟到达3号景点。
第1个旅客旅行时间7 - 0 = 7分钟;
第2个旅客旅行时间2 - 1 = 1分钟;
第3个旅客旅行时间7 - 5 = 2分钟。
总时间7 + 1 + 2 = 10分钟。
数据范围:
对于10%的数据,k = 0;
对于20%的数据,k = 1;
对于40%的数据,2 ≤ n ≤ 50,1 ≤ m ≤ 1,000,0 ≤ k ≤ 20,0 ≤ Di ≤ 10,0 ≤ Ti ≤ 500;
对于60%的数据,1 ≤ n ≤ 100,1 ≤ m ≤ 1,000,0 ≤ k ≤ 100,0 ≤ Di ≤ 100,0 ≤ Ti ≤ 10,000;
对于100%的数据,1 ≤ n ≤ 1,000,1 ≤ m ≤ 10,000,0 ≤ k ≤ 100,000,0 ≤ Di ≤ 100,0 ≤ Ti ≤ 100,000。

【题目链接】:http://noi.qz5z.com/viewtask.asp?id=z06

【题解】

如果不考虑有加速器的情况;
那么设f[i]为公交车到达第i个站的时间;
last[i]表示第i个站最晚上车的人的时间;->这个东西很好搞出来的.

f[i] = max(f[i-1],last[i-1])+d[i-1];
f[1] = 0;
通过这个递推可以得到f[1..n]的值;
然后对于每个乘客
sum+=f[b[i]]-t[i];
就是所有乘客不用加速器的总时间了;
接下来考虑用加速器的情况.
这里我们只考虑一个加速器.
k个的话,叠加一下就好;
如果我们在第i-1个地方用1个加速器;
那么d[i-1]会减少1;
则根据f[i] = max(f[i-1],last[i-1])+d[i-1]可知
f[i]也会减少1(max里面的东西都不变);
那么f[i+1]会怎么变呢?
f[i+1] = max(f[i],last[i])+d[i];
这里的f[i]是减少1了,但是你不能保证f[i]比last[i]大。
这里
如果f[i]>last[i]
那么f[i+1]也会减少1.
以此类推
只要f[i]>last[i],那么这个影响就会一直传递下去.
如果不满足f[i]>last[i],那么这个影响就此消失。此后都不会有影响.
即在第i-1的位置用加速器,影响到了f[i..j];使它们的值都减少了1;
这个影响的范围用g[i]表示->在第i个位置用一次加速器,它能影响到的最右端点;

g[n-1] = n;
if (f[i+1]>last[i+1])
****g[i] = g[i+1]
else
****g[i] = i+1;->无论如何肯定是会影响到下一个站的.
看看我们当时是怎么求总时间的?
for (int i = 1;i <= n;i++) sum+=f[b[i]]-t[i];
而我们在第i个站用加速器会让
[i+1,g[i]]这个区间里面的f值都减1;而所有人的t值不变.
所以我们要求的就是有多少个人的b(也即下车的位置)是在i+1到g[i]这个区间里面的;其人数对应的就是需要减去的时间;(因为只用了一个加速器)
这个可以用前缀和搞;->pre[i]表示在i位置以前下车的人有多少.
则寻找使得pre[g[i]]-pre[i]最大的i;(不要忘了dis[i]要大于0!)
然后dis[i]-=1;
sum-=(pre[g[i]]-pre[i])*1
再重新搞一遍f数组,然后重复k次;
(有些人可能在i+1..g[i]这个区间里面上车,但是没有在g[i]之前下车,那么他们的时间不会减少的.因为它们最终下车的站x,f[x]的值不变,(x>g[i]),也就是它们最终到达时间不变.而t即上车时间不变,那么他们并没有因此而获益,这是因为它们要在g[i]那个站(f[i]<=last[i]),等到last[i]才能走,加速器让f[i]减1了,但是此时f[i]

#include <cstdio>
#include <algorithm>
#define rei(x) scanf("%d",&x)
#define rep1(i,x,y) for (int i = x;i <= y;i++)

using namespace std;

const int MAXN = 1e3+100;
const int MAXM = 1e4+100;

int n,m,k;
int dis[MAXN],t[MAXM],a[MAXM],b[MAXM],pre[MAXN];
int f[MAXN],last[MAXN],sum =0,g[MAXN];

int main()
{
    //freopen("F:\rush.txt","r",stdin);
    rei(n);rei(m);rei(k);
    rep1(i,1,n-1)
        rei(dis[i]);
    rep1(i,1,m)
        {
            rei(t[i]);rei(a[i]);rei(b[i]);
            last[a[i]] = max(last[a[i]],t[i]);
            pre[b[i]]++;
        }
    rep1(i,1,n)
        pre[i] += pre[i-1];
    f[1] = 0;
    rep1(i,2,n)
        f[i] = max(f[i-1],last[i-1]) + dis[i-1];
    rep1(i,1,m)
        sum += f[b[i]]-t[i];
    while (k--)
    {
        g[n-1] = n;
        for (int i = n-2;i >= 1;i--)
            if (f[i+1]>last[i+1])
                g[i] = g[i+1];
            else
                g[i] = i + 1;
        int j = -1,ma = 0;
        rep1(i,1,n-1)
            if (pre[g[i]]-pre[i] > ma && dis[i])
                ma = pre[g[i]]-pre[i],j = i;
        if (j==-1)
            break;
        dis[j]--;
        sum-=pre[g[j]]-pre[j];
        rep1(i,2,n)
            f[i] = max(f[i-1],last[i-1]) + dis[i-1];
    }
    printf("%d
",sum);
    return 0;
}
原文地址:https://www.cnblogs.com/AWCXV/p/7626700.html