pku2125 最小割建模 + dfs 查找割边

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

 

你可能感兴趣的:(DFS)