【题目来源】http://www.spoj.com/problems/COCONUTS/
【问题描述】N个城堡守卫正在就非洲的燕子能否搬运椰子而进行投票。每个人都有自己的看法,但是为了避免跟自己的朋友持相反意见,他们时常会投相反的票。现在给出每个人的初始看法以及朋友关系,求在某种投票方案下,违背自己意愿的票数与持不同意见的朋友对数的总和最小。(2 <= N <= 300, 1 <= M <= N(N-1)/2)
【问题分析】本题属于二种决策选一的经典最小割模型。设立源点S和汇点T,把每个城堡守卫看成点,连接S到每个守卫再到汇点T。如果这个守卫自己的看法为赞同,那么就边守卫->T的容量为1,边S->守卫的容量为0;反之亦然。对于每对朋友关系,连接i->j和j->i,容量为1。
建模分析 : 割对应实际问题的时候,相当于选择边加入割集,并且要使得不存在一条从S->T的路径。倘若选择了S->守卫,表示选择了赞同,边上的容量即为选择赞同的代价,如果这个人原先是想投赞同票,那么边上容量为0,也就相当于没有代价;反之,如果这个人原先是想投反对票,那么边上的容量为1,相当于记录下了他违背自己意愿。并且,对于一对朋友之间,如果2个人的选择不同,那么仍然会存在一条从S->T的路径。此时需要再选取一条边,就是这对朋友i和j之间边的容量,也就是这对朋友选择不同的代价。
【代码如下】
1 #include <iostream> 2 #include <cstdio> 3 #include <climits> 4 #include <cstring> 5 #include <vector> 6 #include <deque> 7 #include <algorithm> 8 9 #define FILE_IO 10 11 using namespace std; 12 13 const int Maxn = 302, INF = INT_MAX; 14 15 struct edge 16 { 17 int v, c; 18 edge* next, * op; 19 edge(int _v, int _c, edge* _next) : v(_v), c(_c), next(_next) {} 20 }* E[Maxn], * Et[Maxn]; 21 22 int S, T, Maxflow, N, M; 23 vector <int> Lv; 24 deque <int> Q; 25 26 void Init(); 27 inline void edgeAdd(int, int, int); 28 void Dinic(); 29 bool Dinic_Label(); 30 int Dinic_Augment(int, int); 31 void Print(); 32 void Clear(); 33 34 int main() 35 { 36 #ifdef FILE_IO 37 freopen("1693.in", "r", stdin); 38 #endif // FILE_IO 39 scanf("%d%d", &N, &M); 40 while (!(N == 0 && M == 0)) 41 { 42 Init(); 43 Dinic(); 44 Print(); 45 Clear(); 46 scanf("%d%d", &N, &M); 47 } 48 #ifdef FILE_IO 49 fclose(stdin); 50 #endif // FILE_IO 51 return 0; 52 } 53 54 void Init() 55 { 56 S = 0; T = N + 1; 57 for (int i = 1; i <= N; i ++) 58 { 59 int a; scanf("%d", &a); 60 if (a) edgeAdd(S, i, 1); 61 else edgeAdd(i, T, 1); 62 } 63 for (int i = 1; i <= M; i ++) 64 { 65 int x, y; scanf("%d%d", &x, &y); 66 edgeAdd(x, y, 1); edgeAdd(y, x, 1); 67 } 68 } 69 70 inline void edgeAdd(int x, int y, int c) 71 { 72 E[x] = new edge(y, c, E[x]); 73 E[y] = new edge(x, 0, E[y]); 74 E[x] -> op = E[y]; E[y] -> op = E[x]; 75 } 76 77 void Dinic() 78 { 79 while (Dinic_Label()) 80 { 81 memcpy(Et, E, sizeof(Et)); 82 Maxflow += Dinic_Augment(S, INF); 83 } 84 } 85 86 bool Dinic_Label() 87 { 88 Lv.assign(T + 1, -1); Lv[S] = 0; 89 Q.clear(); Q.push_back(S); 90 while (!Q.empty()) 91 { 92 int i = Q.front(); Q.pop_front(); 93 for (edge* j = E[i]; j; j = j -> next) 94 { 95 if (j -> c && Lv[j -> v] == -1) 96 { 97 Lv[j -> v] = Lv[i] + 1; 98 if (j -> v == T) return true; 99 Q.push_back(j -> v); 100 } 101 } 102 } 103 return false; 104 } 105 106 int Dinic_Augment(int i, int bm) 107 { 108 if (i == T || bm == 0) return bm; 109 int iflow = 0; 110 for (edge* &j = Et[i]; j; j = j -> next) 111 { 112 if (j -> c && Lv[i] + 1 == Lv[j -> v]) 113 { 114 int add = Dinic_Augment(j -> v, min(bm, j ->c)); 115 j -> c -= add; 116 j -> op -> c += add; 117 iflow += add; 118 bm -= add; 119 if (bm == 0) break; 120 } 121 } 122 return iflow; 123 } 124 125 void Print() 126 { 127 printf("%d\n", Maxflow); 128 } 129 130 void Clear() 131 { 132 for (int i = 0; i <= T; i ++) E[i] = NULL; 133 Maxflow = 0; 134 }