题目大意
杭州有很多共享单车服务点,这些点可以借车也可以还车。还有一个共享单车管理点(PBMC)负责管理每个服务点
这个点不能借车也不能还车,现在规定一个每个服务点的最大容量
C
C
C
m
a
x
max
max , 如果每个服务点的当前车的数量为最大容量的一半就是最佳状态,如果有一个服务点(
S
p
S_p
Sp)的车的数量达到最大或者为0,那么PBMC会去拿走或携带一些车去调整
S
p
S_p
Sp达到最佳状态,同时从PBMC到
S
p
S_p
Sp上的所有服务点都会被调整至最佳状态。
现在需要找出这样一条路使PBMC到
S
p
S_p
Sp的路程最短,假如有多条,则选择需要携带的车的数量最小的一条,如果还是有多条,就选择拿走车的数量最小的一条。输入保证满足条件的答案的唯一性
输入
每组包含一个测试用例,每个用例的第一行是四个数字: C C C m a x max max ( ≤ 100 \leq 100 ≤100, 且总是偶数), N N N( ≤ 500 \leq 500 ≤500, 服务点的数量), S p S_p Sp(所有服务点被编号为1~ N N N,PBMC被编号为0), M M M(路的数量)。第二行是1~ N N N个点的当前车的数量。之后有 M M M行,每行有3个数字 a , b , c a, b, c a,b,c表示 a a a点和 b b b点之间的路程是 c c c
输出
对每个测试样例,在一行之中输出结果。先输出PBMC出发时需要携带的车的数量,在一个空格后输出路径( 0 − > S 1 − > S 2 − > . . . − > S p 0->S_1->S_2->...->S_p 0−>S1−>S2−>...−>Sp), 再在一个空格后输出PBMC返回时需要拿走的车
样例输入
10 3 3 5
6 7 0
0 1 1
0 2 1
0 3 3
1 3 1
2 3 1
样例输出
3 0->2->3 0
解析
题意中有几点要注意:
- 可以从路径中的服务点拿车放到另外的服务点,但是只能从先遇到的服务点拿到后遇到的
- 管理人员从 S p S_p Sp返回的时候不会做任何操作
这一题一开始很容易想到dijkstra,然后又要输出路径,所以就想到保存遍历的最短路径上每一个点的前驱。
这样就等于找到了所有的最短路径,最后还要找出符合条件的唯一一条,所以可以在dfs时进行判断
INF = 0xffffffff
cmax, n, sp, m = 0, 0, 0, 0
edge = list() #存放边
minneed, minback = INF, INF #最小的携带量和拿走量
pre = list() #存放每个点的前驱
dis = list() #存放每个点到PBMC的距离
weight = list() #存放每个点的车的数量-cmax//2
ans, temp = list(), list() #符合要求的路径,临时路径
def dfs(node):
global temp, weight, pre, minneed, minback, ans
temp.append(node)
if node == 0: #表示已经找到一条最短路径
need, back = 0, 0
for i in range(len(temp) - 2, -1, -1):
if weight[temp[i] - 1] > 0:
back += weight[temp[i] - 1]
else:
if weight[temp[i] - 1] + back >= 0:
back += weight[temp[i] - 1]
else:
need = need - (back + weight[temp[i] - 1])
back = 0
if need < minneed: #更新要求的路径
minneed = need
minback = back
ans = list(temp)
elif need == minneed and back < minback:
minback = back
ans = list(temp)
temp.pop()
return
else:
for i in range(len(pre[node])):
dfs(pre[node][i])
temp.pop()
return
def solve():
global cmax, n, sp, m, weight, dis, pre, minneed, minback, ans
cmax, n, sp, m = map(int, input().split())
vis = [False for i in range(n + 1)]
edge = [[INF for i in range(n + 1)] for j in range(n + 1)]
weight = list(map(int, input().split()))
for i in range(n):
weight[i] = weight[i] - cmax // 2 #方便计算和判断
for i in range(m):
a, b, c = map(int, input().split())
edge[a][b] = edge[b][a] = c
dis = [INF for i in range(n + 1)]
dis[0] = 0
pre = [[] for i in range(n + 1)]
for i in range(n + 1): #dijkstra找最短路径和路径的前驱
mark, mindis = -1, INF
for j in range(n + 1):
if (not vis[j]) and dis[j] < mindis:
mark = j
mindis = dis[j]
if mark == -1:
break
vis[mark] = True
for j in range(n + 1):
if not vis[j]:
if dis[mark] + edge[mark][j] < dis[j]:
dis[j] = dis[mark] + edge[mark][j]
pre[j].clear()
pre[j].append(mark)
elif dis[mark] + edge[mark][j] == dis[j]:
pre[j].append(mark)
dfs(sp)
print("%d 0" % minneed, end="")
for i in range(len(ans) - 2, -1, -1):
print("->%d" % ans[i], end="")
print(" %d" % minback)
if __name__ == "__main__":
solve()