HIT_2543
实际每条边一共有两种费用,一种是为0,另一种是c2,而且为0的费用对应的容量是c1,为c2的费用对应的容量是INF。既然一条边有着两条边的属性,不如索性拆成两条边算了,一条容量c1费用为0的边,和一条容量为INF费用为c2的边。
由于c2>=0,我们在做费用流的时候会优先选择容量c1费用为0的边,这样就保证了结果是正确的。
#include<stdio.h> #include<string.h> #include<algorithm> #define MAXD 1010 #define MAXM 80010 #define INF 0x3f3f3f3f int N, M, C, P, first[MAXD], e, next[MAXM], u[MAXM], v[MAXM], flow[MAXM], cost[MAXM]; int S, T, q[MAXD], pre[MAXD], dis[MAXD], inq[MAXD]; const int Q = 1000; void add(int x, int y, int f, int c) { u[e] = x, v[e] = y, flow[e] = f, cost[e] = c; next[e] = first[x], first[x] = e ++; } void init() { int i, x, y, c1, c2; scanf("%d%d%d%d", &N, &M, &C, &P); S = N, T = 1; memset(first, -1, sizeof(first[0]) * (N + 1)); e = 0; add(S, 0, INF, P), add(0, S, 0, -P); for(i = 0; i < M; i ++) { scanf("%d%d%d%d", &x, &y, &c1, &c2); add(x, y, c1, 0), add(y, x, 0, 0), add(y, x, c1, 0), add(x, y, 0, 0); add(x, y, INF, c2), add(y, x, 0, -c2), add(y, x, INF, c2), add(x, y, 0, -c2); } } int bfs() { int i, j, x, front, rear; front = rear = 0; memset(dis, 0x3f, sizeof(dis[0]) * (N + 1)); memset(inq, 0, sizeof(inq[0]) * (N + 1)); dis[S] = 0, pre[S] = -1, q[rear ++] = S; while(front != rear) { x = q[front ++], inq[x] = 0; front > Q ? front = 0 : 0; for(i = first[x]; i != -1; i = next[i]) if(flow[i] && dis[x] + cost[i] < dis[v[i]]) { dis[v[i]] = dis[x] + cost[i], pre[v[i]] = i; if(!inq[v[i]]) { q[rear ++] = v[i], inq[v[i]] = 1; rear > Q ? rear = 0 : 0; } } } return dis[T] != INF; } void solve() { int i, a, f = 0, c = C; while(bfs()) { for(i = pre[T], a = INF; i != -1; i = pre[u[i]]) a = std::min(a, flow[i]); if((long long)a * dis[T] > c) { f += c / dis[T]; break; } for(i = pre[T]; i != -1; i = pre[u[i]]) flow[i] -= a, flow[i ^ 1] += a; f += a, c -= a * dis[T]; } printf("%d\n", f); } int main() { int t; scanf("%d", &t); while(t --) { init(); solve(); } return 0; }