Destroying The Graph

题目大意:给定一个图,给每个顶点两个操作:删除顶点 i 的所有入边,需要花费W+,删除顶点 i 的所有出边,需要花费W-,现有一个图,可能含有圈和重边,问:删除所有的边,需要的最小花费,并输出操作过程。

分析:既然删除出边和删除入边花费不一样,就可以拆点,对任意一点 u 拆成 u 和 u' 两个点,u 负责入边,u' 负责出边,u 和 u'之间无边相连, 这样 u' 就只有出边,u 就只有入边,就构成了二分图,所有 u' 点构成了 X 集合,所有 u 点构成了 Y 集合,这样就可以用我另外一篇随笔的做法求解 【论】如何将二分图顶点覆盖问题转化为最小割

这题在求出最小割后,需要输出中间的操作过程,就是删除了哪些边。这个可以用一个DFS求得,既然已经求得最小割,则s到t已经不在连通,在残余网络里有S能搜到的所有点即为割[S, T]里的S集合。在搜到的这些点里,如果在 X 集合,则删除的是出边,在为搜到的点里,如果是在 Y 集合,删除的就是入边。

 

代码
1 #include<stdio.h>
2 #include<string.h>
3  #define INF 0x3fffffff
4  #define MM 10410
5 #define NN 204
6 typedef struct node{
7 int v, c;
8 struct node *nxt, *op;
9 }NODE;
10
11 NODE edg[MM];
12 NODE *Link[NN];
13
14 int idx, n, S, T, N, M;
15 int wIn[104]; // 入边权
16 int wOut[104]; // 出边权
17 int e[5004][2]; //
18 int h[NN]; // 距离标号
19 int vh[NN]; // gap 优化
20 int mark[NN];
21
22 void Add(int u, int v, int c1, int c2){// 加边
23 idx++;
24 edg[idx].v = v;
25 edg[idx].c = c1;
26 edg[idx].nxt = Link[u];
27 edg[idx].op = edg + idx + 1;
28 Link[u] = edg + idx;
29
30 idx++;
31 edg[idx].v = u;
32 edg[idx].c = c2;
33 edg[idx].nxt = Link[v];
34 edg[idx].op = edg + idx - 1;
35 Link[v] = edg + idx;
36
37 }
38 void CreatGraph(){// 建图
39 int i, ii;
40 S = 0;
41 T = 2 * N + 1;
42 idx = 0;
43 for (i = 1; i <= N; i++){
44 ii = i + N;
45 Add(S, ii, wOut[i], 0);
46 Add(i, T, wIn[i], 0);
47 }
48 for (i = 1; i <= M; i++){
49 ii = e[i][0] + N;
50 Add(ii, e[i][1], INF, 0);
51 }
52 }
53
54 int aug(int u, int flow){ // 增广找最大流
55 if (u == T) return flow;
56 int l = flow;
57 int tmp = n - 1;
58 int v, f;
59 for (NODE *p = Link[u]; p; p = p->nxt){
60 v = p->v;
61 if (p->c && h[u] == h[v] + 1){
62 f = aug(v, p->c < l ? p->c : l);
63 l -= f;
64 p->c -= f;
65 p->op->c += f;
66 if (!l || h[S] == n) return flow - l;
67 }
68 if (p->c && h[v] < tmp) tmp = h[v];
69 }
70 if (l == flow)
71 if (!--vh[h[u]]) h[S] = n;
72 else ++ vh[h[u] = tmp + 1];
73 return flow - l;
74 }
75
76 void Sap(){ // sap求最大流
77 int ans = 0;
78 n = T + 1;
79 memset(h, 0, sizeof(h));
80 memset(vh, 0, sizeof(vh));
81 vh[0] = n;
82 while(h[S] < n){
83 ans += aug(S, INF);
84 }
85 printf("%d\n", ans);
86 }
87
88 void dfs(int u){ // 对于最小割[S,T],找到S中点集用mark[]数组标记
89 int v;
90 for (NODE *p = Link[u]; p; p = p->nxt){
91 v = p->v;
92 if (mark[v]) continue;
93 if (p->c){
94 mark[v] = 1;
95 dfs(v);
96 }
97 }
98 }
99 int main()
100 {
101 int i, num;
102 int cnt[NN];
103 scanf("%d%d", &N, &M);
104 for (i = 1; i <= N; i++){
105 scanf("%d", &wIn[i]);
106 }
107 for (i = 1; i <= N; i++){
108 scanf("%d", &wOut[i]);
109 }
110 for (i = 1; i <= M; i++){
111 scanf("%d%d", &e[i][0], &e[i][1]);
112 }
113 memset(Link, 0, sizeof(Link));
114 CreatGraph();
115 Sap();
116 memset(mark, 0, sizeof(mark));
117 mark[S] = 1;
118 dfs(S);
119 num = 0;// 搜到的S集合里的点
120 for (i = 1; i <= 2 * N; i++){ // 查找割边
121 if (!mark[i] && i > N){
122 cnt[++num] = i; // 如果是在X集合
123 }
124 if (mark[i] && i <= N){
125 cnt[++num] = i;// 在Y集合
126 }
127 }
128 printf("%d\n", num);
129 for (i = 1; i <= num; i++){
130 if (cnt[i] > N){
131 printf("%d -\n", cnt[i] - N);
132 }else{
133 printf("%d +\n", cnt[i]);
134 }
135 }
136 return 0;
137 }
138

 

posted on 2010-08-17 08:10  ylfdrib  阅读(844)  评论(1)    收藏  举报