【题意】:给出一个有向图,可能有平行边和环。对于图上的每一个点i,给出两个费用Wi+和Wi-,Wi+代表删除点i所有入边的费用,Wi-代表删除点i所有出边的费用。求删除改图所有边的最小费用是多少?并输出割边集。

【题解】:我们先来分析一下,对于一条有向边<u,v>,要删除它的话显然有两种方法,要么删除u的出边,要么删除v的入边,这是一个选择性问题。对于费用最小加上选择性问题,我们很容易就想到最小割模型。构图:对于每个点i,拆成 i 和 i' ,从源点s向 i 连边,容量为Wi-;从 i' 向汇点t连边,容量为Wi+。对于原图的边<u,v>,连边u->v',容量为inf,保证这条边不会被割掉。最后,最小割就是最大流。题目中还要求我们输出割边,可以从源点s进行一次dfs染色,这样S集和T集的颜色必然不同,如果某一条边两端点颜色不同,即为割边。

【代码】:

  1 #include "iostream"
  2 #include "cstdio"
  3 #include "cstring"
  4 using namespace std;
  5 const int inf = 1 << 30;
  6 #define maxn 250
  7 #define maxm 15000
  8 int n, m, s, t, k;
  9 int out[maxn], in[maxn];
 10 int eh[maxn], pre[maxn], cur[maxn], dist[maxn], tot, cnt[maxn], low[maxn];
 11 int col[maxn];
 12 struct Edge {
 13     int u, v, cap, flow, next;
 14 }et[maxm];
 15 
 16 void init() {
 17     tot = k = 0;
 18     memset(eh, -1sizeof(eh));
 19     memset(col, 0sizeof(col));
 20 }
 21 
 22 void add(int u, int v, int cap, int flow) {
 23     Edge e = {u, v, cap, flow, eh[u]};
 24     et[tot] = e;
 25     eh[u] = tot++;
 26 }
 27 
 28 void addedge(int u, int v, int cap) {
 29     add(u, v, cap, 0), add(v, u, 00);
 30 }
 31 
 32 int isap(int s, int t, int n) {
 33     int u, v, now, flow = 0;
 34     memset(dist, 0sizeof(dist));
 35     memset(low, 0sizeof(low));
 36     memset(cnt, 0sizeof(cnt));
 37     for(u = 0; u <= n; u++) cur[u] = eh[u];
 38     low[s] = inf, cnt[0= n;
 39     u = s;
 40     while(dist[s] < n) {
 41         for(now = cur[u]; now != -1; now = et[now].next)
 42             if(et[now].cap - et[now].flow && dist[u] == dist[v = et[now].v] + 1)
 43                 break;
 44         if(now != -1) {
 45             cur[u] = pre[v] = now;
 46             low[v] = min(low[u], et[now].cap - et[now].flow);
 47             u = v;
 48             if(u == t) {
 49                 for(; u != s; u = et[pre[u]].u) {
 50                     et[pre[u]].flow += low[t];
 51                     et[pre[u]^1].flow -= low[t];
 52                 }
 53                 low[s] = inf, flow += low[t];
 54             }
 55         } else {
 56             if(--cnt[dist[u]] == 0break;
 57             dist[u] = n;
 58             cur[u] = eh[u];
 59             for(now = eh[u]; now != -1; now = et[now].next)
 60                 if(et[now].cap - et[now].flow && dist[u] > dist[et[now].v] + 1)
 61                     dist[u] = dist[et[now].v] + 1;
 62             cnt[dist[u]]++;
 63             if(u != s) u = et[pre[u]].u;
 64         }
 65     }
 66     return flow;
 67 }
 68 
 69 void dfs(int u) {
 70     col[u] = 1;
 71     for(int i = eh[u]; i != -1; i = et[i].next) {
 72         int v = et[i].v;
 73         if(et[i].cap - et[i].flow && !col[v])
 74             dfs(v);
 75     }
 76 }
 77 
 78 int main() {
 79     while(~scanf("%d%d"&n, &m)) {
 80         init();
 81         s = 0, t = n * 2 + 1;
 82         for(int i = 1; i <= n; i++) scanf("%d"&in[i]), addedge(n + i, t, in[i]);
 83         for(int i = 1; i <= n; i++) scanf("%d"&out[i]), addedge(s, i, out[i]);
 84         for(int i = 1; i <= m; i++) {
 85             int u, v;
 86             scanf("%d%d"&u, &v);
 87             addedge(u, n + v, inf);
 88         }
 89         printf("%d\n", isap(s, t, t - s + 1));
 90         dfs(s);
 91         for(int i = 1; i <= n; i++) {
 92             if(col[i] == 0) k++;
 93             if(col[i + n] == 1) k++;
 94         }
 95         printf("%d\n", k);
 96         for(int i = 1; i <= n; i++) {
 97             if(col[i] == 0) printf("%d -\n", i);
 98             if(col[i + n] == 1) printf("%d +\n", i);
 99         }
100     }
101     return 0;
102 }