题目链接:点击打开链接
题意:
给定n个点m条有向边的图, 起点s ,终点t
下面m条边 u,v, a,b 若选择这条边花费为a, 不选择花费为b
构造一条欧拉通路使得起点是s,终点是t,且花费最小。
思路:
首先能想到一个简单的思路:假设所有边都选择,然后在费用流里跑,就会出现负环的问题。。
所以为了避免负环,让所有费用都为正值:
int sum = 0;
若a>b, 则 费用要为正只能是 a-b, 默认这条边是删除是, sum += b, 那么如果我们要选择这条边,则从u=>v, 费用为a-b, 流量为1即可
若a<b, 则费用要为正只能是b-a, 默认这条边是选择是, sum += a, 那么如果我们要删除这条边,则相当于选一条反向的边,即v=>u,费用为b-a, 流量为1
因为我们要保持流量统一,所以用炒鸡汇源来维持,
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <iostream>
template <class T>
inline bool rd(T &ret) {
char c; int sgn;
if (c = getchar(), c == EOF) return 0;
while (c != '-' && (c<'0' || c>'9')) c = getchar();
sgn = (c == '-') ? -1 : 1;
ret = (c == '-') ? 0 : (c - '0');
while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
ret *= sgn;
return 1;
}
template <class T>
inline void pt(T x) {
if (x <0) {
putchar('-');
x = -x;
}
if (x>9) pt(x / 10);
putchar(x % 10 + '0');
}
using namespace std;
typedef int ll;
#define inf 0x3f3f3f3f
#define N 300
#define M 605*605*4
struct Edge {
ll to, cap, cost, nex;
Edge(){}
Edge(ll to, ll cap, ll cost, ll next) :to(to), cap(cap), cost(cost), nex(next){}
} edge[M << 1];
ll head[N], edgenum;
ll D[N], A[N], P[N];
bool inq[N];
void add(ll from, ll to, ll cap, ll cost) {
edge[edgenum] = Edge(to, cap, cost, head[from]);
head[from] = edgenum++;
edge[edgenum] = Edge(from, 0, -cost, head[to]);
head[to] = edgenum++;
}
bool spfa(ll s, ll t, ll &flow, ll &cost) {
for (ll i = 0; i <= t; i++) D[i] = inf;
memset(inq, 0, sizeof inq);
queue<ll>q;
q.push(s);
D[s] = 0; A[s] = inf;
while (!q.empty()) {
ll u = q.front(); q.pop();
inq[u] = 0;
for (ll i = head[u]; ~i; i = edge[i].nex)
{
Edge &e = edge[i];
if (e.cap && D[e.to] > D[u] + e.cost)
{
D[e.to] = D[u] + e.cost;
P[e.to] = i;
A[e.to] = min(A[u], e.cap);
if (!inq[e.to])
{
inq[e.to] = 1; q.push(e.to);
}
}
}
}
//若费用为inf则中止费用流
if (D[t] == inf) return false;
cost += D[t] * A[t];
flow += A[t];
ll u = t;
while (u != s) {
edge[P[u]].cap -= A[t];
edge[P[u] ^ 1].cap += A[t];
u = edge[P[u] ^ 1].to;
}
return true;
}
ll flow, cost;
ll Mincost(ll s, ll t){
flow = 0, cost = 0;
while (spfa(s, t, flow, cost));
return cost;
}
void init(){ memset(head, -1, sizeof head); edgenum = 0; }
int n, m, s, t, from, to;
int in[N], out[N];
int main(){
int T, Cas = 1; rd(T);
while (T--){
memset(in, 0, sizeof in);
memset(out, 0, sizeof out);
init();
rd(n); rd(m); rd(s); rd(t);
from = 0, to = n + 1;
ll now = 0;
for (int i = 0, u, v, a, b; i < m; i++){
rd(u); rd(v); rd(a); rd(b);
if (a > b){
now += b;
add(u, v, 1, a - b);
}
else {
now += a;
add(v, u, 1, b - a);
in[v]++; out[u]++;
}
}
in[s]++; out[t]++;
for (int i = 1; i <= n; i++){
if (in[i] > out[i])
add(from, i, in[i] - out[i], 0);
else if (in[i] < out[i])
add(i, to, out[i] - in[i], 0);
}
Mincost(from, to);
printf("Case %d: ", Cas++);
for (int i = head[from]; ~i; i = edge[i].nex){
if (edge[i].cap)flow = -1;
}
if (flow == -1)puts("impossible");
else printf("%d\n", now + cost);
}
return 0;
}