题目
题目太长请自行阅读。
风景迷人的小城
Y
Y
Y市,拥有
n
n
n个美丽的景点。由于慕名而来的游客越来越多,
Y
Y
Y市特意安排了一辆观光公交车,为游客提供更便捷的交通服务。观光公交车在第
0
0
0分钟出现在
1
1
1号景点,随后依次前往
2
、
3
、
4
…
…
n
2、3、4……n
2、3、4……n号景点。从第
i
i
i号景点开到第
i
+
1
i+1
i+1号景点需要
D
i
D_i
Di 分钟。任意时刻,公交车只能往前开,或在景点处等待。
设共有
m
m
m个游客,每位游客需要乘车
1
1
1次从一个景点到达另一个景点,第
i
i
i位游客在
T
i
T_i
Ti 分钟来到景点
A
i
A_i
Ai,希望乘车前往景点
B
i
(
A
i
<
B
i
)
B_i(Ai<Bi)
Bi(Ai<Bi)。为了使所有乘客都能顺利到达目的地,公交车在每站都必须等待需要从该景点出发的所有乘客都上车后才能出发开往下一景点。假设乘客上下车不需要时间。
一个乘客的旅行时间,等于他到达目的地的时刻减去他来到出发地的时刻。因为只有一辆观光车,有时候还要停下来等其他乘客,乘客们纷纷抱怨旅行时间太长了。于是聪明的司机
Z
Z
ZZ
ZZ 给公交车安装了
k
k
k个氮气加速器,每使用一个加速器,可以使其中一个
D
i
D_i
Di减
1
1
1。对于同一个
D
i
D_i
Di可以重复使用加速器,但是必须保证使用后
D
i
Di
Di大于等于
0
0
0。
那么
Z
Z
ZZ
ZZ该如何安排使用加速器,才能使所有乘客的旅行时间总和最小?
思路
先求出不加速的答案,再用费用流求出加速可以减去的时间,最后就可以得出答案了。
首先我们要求出几个东西
l
a
s
t
last
last,
d
o
w
n
down
down和
g
e
t
get
get。
其中
l
a
s
t
i
last_i
lasti表示做第
i
i
i站最晚乘客的到达时间。
d
o
w
n
i
down_i
downi表示第
i
i
i站下车乘客的数量。
g
e
t
i
get_i
geti表示第
i
i
i站车到达的时间。
那么不加速的答案就为
∑
i
=
1
m
g
e
t
p
i
.
b
−
p
i
.
t
\sum_{i=1}^{m}get_{p_i.b}-p_i.t
∑i=1mgetpi.b−pi.t。
考虑费用流,我们这样构图:
s
→
s
′
s \rightarrow s'
s→s′,容量为
k
k
k,费用为
0
0
0,限制了加速器的个数。
对于每一个点,我们把它们拆成
i
i
i和
i
′
i'
i′。
s
′
→
i
′
s' \rightarrow i'
s′→i′,容量为
d
i
d_i
di,费用为
0
0
0,表示最多用
d
i
d_i
di个氮气。
i
→
i
′
i \rightarrow i'
i→i′,容量为
m
a
x
(
g
e
t
i
−
l
a
s
t
i
,
0
)
max(get_i-last_i,0)
max(geti−lasti,0),费用为
0
0
0,代表这个站可以使用这么多个氮气加速使得后面能受到影响,因为过早地到达也要等待巴士到来。
i
′
→
i
+
1
i' \rightarrow i+1
i′→i+1,容量为
i
n
f
inf
inf,费用为
d
o
w
n
i
+
1
down_{i+1}
downi+1,表示接受氮气可以使经过这条路的人都减少
1
1
1个时间。
i
+
1
→
t
i+1\rightarrow t
i+1→t,容量为
i
n
f
inf
inf,费用为
0
0
0,把流量流到汇点。
代码
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
struct people{
int t, a, b;
}p[100001];
struct node{
int x, y, next, cost, flow;
}e[8001];
int n, m, k, ans, tot = 1;
int s, s1, t;
int d[1001], last[1001], get[1001], down[1001], head[2004], pre[2004], dis[2004], v[2004];
const int inf = 2147483647;
void add(int x, int y, int flow, int cost) {
e[++tot].x = x;
e[tot].y = y;
e[tot].cost = cost;
e[tot].flow = flow;
e[tot].next = head[x];
head[x] = tot;
e[++tot].x = y;
e[tot].y = x;
e[tot].cost = -cost;
e[tot].flow = 0;
e[tot].next = head[y];
head[y] = tot;
}
int spfa() {
std::queue<int> q;
int x, y;
memset(dis, 127 / 3, sizeof(dis));
memset(v, 0, sizeof(v));
q.push(s);
dis[s] = 0;
v[s] = 1;
while (q.size()) {
x = q.front();
q.pop();
v[x] = 0;
for (int i = head[x]; i; i = e[i].next) {
if (!e[i].flow) continue;
y = e[i].y;
if (dis[y] > dis[x] + e[i].cost) {
dis[y] = dis[x] + e[i].cost;
pre[y] = i;
if (!v[y]) {
v[y] = 1;
q.push(y);
}
}
}
}
return dis[t] < 707406378;
}
void addflow() {
int i = t, mn = 2147483647;
while (pre[i]) {
mn = std::min(mn, e[pre[i]].flow);
i = e[pre[i]].x;
}
ans += dis[t] * mn;
i = t;
while (pre[i]) {
e[pre[i]].flow -= mn;
e[pre[i] ^ 1].flow += mn;
i = e[pre[i]].x;
}
}
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", &p[i].t, &p[i].a, &p[i].b);
last[p[i].a] = std::max(p[i].t, last[p[i].a]);
down[p[i].b]++;
}
for (int i = 1; i < n; i++)
get[i + 1] = std::max(last[i], get[i]) + d[i];
for (int i = 1; i <= m; i++)
ans += get[p[i].b] - p[i].t;
s = n * 2 + 1;
s1 = n * 2 + 2;
t = n * 2 + 3;
add(s, s1, k, 0);
for (int i = 1; i < n; i++) {
add(s1, i + n, d[i], 0);
add(i + n, i + 1, inf, -down[i + 1]);//改成负的跑最小费用最大流
add(i + 1, t, inf, 0);
add(i, i + n, std::max(get[i] - last[i], 0), 0);
}
while (spfa())
addflow();
printf("%d", ans);
}