题意:
有n个景点,在横线上依次排列。第i个景点到第i+1个景点的距离为D[i]。现在有m个游客,每个游客会到达一个某个景点A,且正好在第T分钟到达,以及这个乘客要在景点B下车(A < B)。现在有一个公交车,从1号景点按标号依次走到n号景点。每到达一个景点,下车和上车不需要时间,但是必须等到所有的人都上车才能离开。景点之间的消耗的时间等于距离。
现在有k个氮气加速器。用在D[i]上可以使D[i]–(不能减成负数)。现在求一个氮气加速器的使用方案,使得所有人的下车时刻-到达车站的时刻的和最小。
数据范围:
对于 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。
思路:
借鉴于:【NOIP2011 day2】观光公交(作者的写作风格很有趣的…)
如标题所言,这道题是贪心。很容易想到加入一个氮气加速器的时候,影响的人越多越好。这样子就暴力模拟这个过程就好了。
每一次使用一个氮气加速器的时候,从后往前扫。算出所有的位置,如果在这个位置i使用加速器使得D[i]- -,能够影响到的人数。然后选择人数最多的i使用氮气加速器。这里有一个小问题(没有考虑下面的Hack数据的时候,考虑了,改了之后,就不需要考虑这个问题了),就是说,当影响到的人数最多的点不唯一的时候,选最左边的那一个。这是因为,如果是选择靠右的那一个的话,它会使得之前的所有的能够影响到的人数可能会减少,所以说需要选靠左的,可能影响到的人数较少。这样子就得到了一个O(nk)的算法。这样居然能过!!
另外还有一个统计总时间的小技巧:
Ans=∑mi=1(ed[i]−T[i])
A
n
s
=
∑
i
=
1
m
(
e
d
[
i
]
−
T
[
i
]
)
=∑mi=1(ed[i])−∑mi=1(T[i])
=
∑
i
=
1
m
(
e
d
[
i
]
)
−
∑
i
=
1
m
(
T
[
i
]
)
=∑ni=1(out[i]∗arrive[i])−∑mi=1(T[i])
=
∑
i
=
1
n
(
o
u
t
[
i
]
∗
a
r
r
i
v
e
[
i
]
)
−
∑
i
=
1
m
(
T
[
i
]
)
这样子分开统计就好了。
但是还有一个问题(建议看了之后的代码之后再来看这一段),那就是不久前有人在洛谷上面,加入了一个新的hack数据,卡掉了上述所说的贪心,这是链接中没有提到的。仔细分析时候,发现其实当D[i]–到了0的时候,第i个位置所影响的人数应当为0,因为既然当前的位置都不能再使用加速器了,当然就无法影响到任何人了。但是这样子就有个问题,那就是i以前的位置还有可能对i以后的位置的人有影响,但是当peo[i]被清成0之后,就无法去更新之前的状态了。那么就需要先去掉D[i]==0的限制去更新玩所有的状态,然后在选取最大影响人数的判断一下D[i]是否=0即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 1000
using namespace std;
int n,m,k;
int D[MAXN+5],T,A,B;
int maxt[MAXN+5],out[MAXN+5],peo[MAXN+5],arr[MAXN+5];
int main()
{
// freopen("bus.in","r",stdin);
// freopen("bus.out","w",stdout);
scanf("%d %d %d",&n,&m,&k);
for(int i=1;i<n;i++)
scanf("%d",&D[i]);
int ans=0;
for(int i=1;i<=m;i++)
{
scanf("%d %d %d",&T,&A,&B);
maxt[A]=max(maxt[A],T);//统计每一个点的最晚上车的人
out[B]++;//统计B点下车的人
ans-=T;//利用之前的式子
}
while(k--)//O(nk)
{
memset(peo,0,sizeof(peo));
for(int i=2;i<=n;i++)
arr[i]=max(arr[i-1],maxt[i-1])+D[i-1];//求到达时间(不包含等待时间)
for(int i=n;i>=2;i--)
{
peo[i-1]=out[i];//首先累加上第i个点的下车人数
if(arr[i]>maxt[i])//注意'='是不可以的,因为一旦-1之后,后面仍然是没有影响的
peo[i-1]+=peo[i];
}
int maxval=0,pos=0;
for(int i=1;i<=n-1;i++)
if(peo[i]>maxval&&D[i]!=0)//前面所说的特殊处理(最后一段)
{
maxval=peo[i];
pos=i;
}
if(maxval==0)
break;
D[pos]--;
}
for(int i=2;i<=n;i++)
arr[i]=max(arr[i-1],maxt[i-1])+D[i-1];
for(int i=1;i<=n;i++)
ans+=arr[i]*out[i];//公式
printf("%d\n",ans);
return 0;
}
/*
Hack数据
4 5 1
3 0 2
0 1 2
0 1 4
0 1 4
0 1 4
0 1 4
*/
如果有不清楚的,可以去参考链接里面的博客。