Description
可可和卡卡家住合肥市的东郊,每天上学他们都要转车多次才能到达市区西端的学校。直到有一天他们两人参加了学校的信息学奥林匹克竞赛小组才发现每天上学的乘车路线不一定是最优的。 可可:“很可能我们在上学的路途上浪费了大量的时间,让我们写一个程序来计算上学需要的最少时间吧!” 合肥市一共设有N个公交车站,不妨将它们编号为1…N的自然数,并认为可可和卡卡家住在1号汽车站附近,而他们学校在N号汽车站。市内有M条直达汽车路线,执行第i条路线的公交车往返于站点pi和qi之间,从起点到终点需要花费的时间为ti。(1<=i<=M, 1<=pi, qi<=N) 两个人坐在电脑前,根据上面的信息很快就编程算出了最优的乘车方案。然而可可忽然有了一个鬼点子,他想趁卡卡不备,在卡卡的输入数据中删去一些路线,从而让卡卡的程序得出的答案大于实际的最短时间。而对于每一条路线i事实上都有一个代价ci:删去路线的ci越大卡卡就越容易发现这个玩笑,可可想知道什么样的删除方案可以达到他的目的而让被删除的公交车路线ci之和最小。 [任务] 编写一个程序: ? 从输入文件中读取合肥市公交路线的信息; ? 计算出实际上可可和卡卡上学需要花费的最少时间; ? 帮助可可设计一个方案,删除输入信息中的一些公交路线,使得删除后从家到学校需要的最少时间变大,而被删除路线的ci和最小;向输出文件输出答案。
Input
输入文件中第一行有两个正整数N和M,分别表示合肥市公交车站和公交汽车路线的个数。以下M行,每行(第i行,总第(i+1)行)用四个正整数描述第i条路线:pi, qi, ti, ci;具体含义见上文描述。
Output
输出文件最多有两行。 第一行中仅有一个整数,表示从可可和卡卡家到学校需要的最短时间。 第二行输出一个整数C,表示Ci之和
Sample Input
6 7
1 2 1 3
2 6 1 5
1 3 1 1
3 4 1 1
4 6 1 1
5 6 1 2
1 5 1 4
Sample Output
2
5
HINT
2<=N<=500, 1<=M<=124 750, 1<=ti, ci<=10 000
合肥市的公交网络十分发达,你可以认为任意两个车站间都可以通过直达或转车互相到达,当然如果在你提供的删除方案中,家和学校无法互相到达,那么则认为上学需要的最短为正无穷大:这显然是一个合法的方案。
题解
首先Dijkstra求出起点到所有点的距离$dis_i$,然后我们发现当前影响最短路长度的边只有$dis_{q_i}=dis_{p_i}+t_i$的边,将保留这些边,求最小割即可。
附代码:
#include <algorithm> #include <cstdio> #include <queue> typedef long long LL; const LL INF = 1000000000000000LL; const int N = 550; const int M = 300000; int pre[N], nxt[M], to[M], t[M], c[M], cnt = 0; inline void addedge(int p, int q, int ti, int ci) { to[cnt] = q; t[cnt] = ti; c[cnt] = ci; nxt[cnt] = pre[p]; pre[p] = cnt++; to[cnt] = p; t[cnt] = ti; c[cnt] = ci; nxt[cnt] = pre[q]; pre[q] = cnt++; } LL dis[N]; int n, m; struct HeapNode{ int x; LL d; HeapNode(int x = 0, LL d = 0) : x(x), d(d) {} inline bool operator<(const HeapNode &y)const{ return d > y.d; } }; void Dijkstra() { static std::priority_queue<HeapNode> Q; std::fill(dis + 1, dis + n + 1, INF); while (!Q.empty()) Q.pop(); Q.push(HeapNode(1, dis[1] = 0)); while (!Q.empty()) { HeapNode x = Q.top(); Q.pop(); if (x.d != dis[x.x]) continue; int u = x.x; if (u == n) return; for (int i = pre[u]; ~i; i = nxt[i]) if (dis[to[i]] > dis[u] + t[i]) Q.push(HeapNode(to[i], dis[to[i]] = dis[u] + t[i])); } } struct Dinic{ int pre[N], nxt[M], to[M], cnt; LL ret[M]; int dis[N]; Dinic() { cnt = 0; std::fill(pre, pre + N, -1); } inline void addedge(int p, int q, LL c) { to[cnt] = q; ret[cnt] = c; nxt[cnt] = pre[p]; pre[p] = cnt++; to[cnt] = p; ret[cnt] = 0; nxt[cnt] = pre[q]; pre[q] = cnt++; } bool BFS() { static std::queue<int> Q; while (!Q.empty()) Q.pop(); std::fill(dis + 1, dis + n + 1, -1); dis[1] = 0; Q.push(1); while (!Q.empty()) { int x = Q.front(); Q.pop(); for (int i = pre[x]; ~i; i = nxt[i]) if (ret[i] && !~dis[to[i]]) { dis[to[i]] = dis[x] + 1; Q.push(to[i]); if (to[i] == n) return true; } } return false; } LL DFS(int x, LL f) { if (x == n) return f; LL ans = 0; for (int i = pre[x]; ~i && f; i = nxt[i]) if (ret[i] && dis[to[i]] == dis[x] + 1) { LL df = DFS(to[i], std::min(f - ans, ret[i])); ans += df; ret[i] -= df; ret[i ^ 1] += df; } if (ans == f) dis[x] = n + 2; return ans; } LL solve() { LL ans = 0; while (BFS()) ans += DFS(1, INF); return ans; } }; Dinic solver; int main() { scanf("%d%d", &n, &m); std::fill(pre + 1, pre + n + 1, -1); int p, q, ti, ci; for (int i = 0; i < m; ++i) { scanf("%d%d%d%d", &p, &q, &ti, &ci); addedge(p, q, ti, ci); } Dijkstra(); for (int i = 0; i < cnt; ++i) if (dis[to[i]] - dis[to[i ^ 1]] == t[i]) solver.addedge(to[i ^ 1], to[i], c[i]); printf("%lld\n%lld\n", dis[n], solver.solve()); return 0; }