BZOJ1934: [Shoi2007]Vote 善意的投票
1934: [Shoi2007]Vote 善意的投票
Time Limit: 1 Sec Memory Limit: 64 MBSubmit: 2489 Solved: 1544
[Submit][Status][Discuss]
Description
幼儿园里有n个小朋友打算通过投票来决定睡不睡午觉。对他们来说,这个问题并不是很重要,于是他们决定发扬谦让精神。虽然每个人都有自己的主见,但是为了
照顾一下自己朋友的想法,他们也可以投和自己本来意愿相反的票。我们定义一次投票的冲突数为好朋友之间发生冲突的总数加上和所有和自己本来意愿发生冲突的
人数。
我们的问题就是,每位小朋友应该怎样投票,才能使冲突数最小?
Input
第一行只有两个整数n,m,保证有
2≤n≤300,1≤m≤n(n-1)/2。其中n代表总人数,m代表好朋友的对数。文件第二行有n个整数,第i个整数代表第i个小朋友的意愿,当它为1
时表示同意睡觉,当它为0时表示反对睡觉。接下来文件还有m行,每行有两个整数i,j。表示i,j是一对好朋友,我们保证任何两对i,j不会重复。
Output
只需要输出一个整数,即可能的最小冲突数。
Sample Input
3 3
1 0 0
1 2
1 3
3 2
1 0 0
1 2
1 3
3 2
Sample Output
1
HINT
在第一个例子中,所有小朋友都投赞成票就能得到最优解
Source
【题解】
本题这样建图:
S连向同意的点,我们把这些边记做s边,由S连出的点叫s点
不同意的点连T,我们把这些边记做t边,连向T的点叫t点
观点相同的朋友之间加双向边,我们把这些边记做sam边
观点不同的朋友之间加双向边,我们把这些边记做dif边
网上大多数题解描述的建图原因:
不妨设割完之后S点所在集合是同意
割掉s边,意味着s点从“同意”变成了“不同意”,增加一点矛盾
割掉t边,意味着t点从“不同意”变成了“同意”,增加一点矛盾
割掉dif边,意味着朋友保留dif边所连点的s边和t边,表示这两个点一个同意一个不同意,增加一点矛盾
仔细想想,这样解释完全是错误的
因为s点或t点立场改变的时候,不只影响dif边,还影响着sam边!原来立场相同的变得立场不同,立场不同的变得立场相同
这就是双向边的原因!
加了双向边,意味着割掉一条s边后,它对应的s点就不需要割对应的两条dif边,但必须把s点连出的f边(一条,从f边两端点的
非s点指向s点的那一条)割去(全部或一部分,取决于f边所连点的割边方式),否则可能出现s->f边连的另一个点->s点->dif边所连的另一个点->T的一条路径,不是割
这样割边就意味着改变了一个点的立场,少割了跟它不同立场的朋友,多割了跟它同立场的朋友
这才是原题的意思
这才是加双向边的原因
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <algorithm> 6 #include <cmath> 7 #include <queue> 8 #include <vector> 9 #define min(a, b) ((a) < (b) ? (a) : (b)) 10 #define max(a, b) ((a) > (b) ? (a) : (b)) 11 #define abs(a) ((a) < 0 ? (-1 * (a)) : (a)) 12 inline void swap(int &a, int &b) 13 { 14 long long tmp = a;a = b;b = tmp; 15 } 16 inline void read(int &x) 17 { 18 x = 0;char ch = getchar(), c = ch; 19 while(ch < '0' || ch > '9') c = ch, ch = getchar(); 20 while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar(); 21 if(c == '-')x = -x; 22 } 23 24 const int INF = 0x3f3f3f3f; 25 const int MAXN = 500; 26 const int MAXM = 1000000; 27 28 struct Edge 29 { 30 int u,v,w,nxt; 31 Edge(int _u, int _v, int _w, int _nxt){u = _u;v = _v;w = _w;nxt = _nxt;} 32 Edge(){} 33 }edge[MAXM << 1]; 34 int head[MAXN], cnt = 1, q[MAXN], h[MAXN], ans = 0, S, T; 35 36 inline void insert(int a, int b, int c) 37 { 38 edge[++cnt] = Edge(a,b,c,head[a]); 39 head[a] = cnt; 40 edge[++cnt] = Edge(b,a,0,head[b]); 41 head[b] = cnt; 42 } 43 44 bool bfs() 45 { 46 int he = 0, ta = 1; 47 memset(h, -1, sizeof(h)); 48 q[he] = S, h[S] = 0; 49 while(he < ta) 50 { 51 int now = q[he ++]; 52 for(int pos = head[now];pos;pos = edge[pos].nxt) 53 { 54 int v = edge[pos].v; 55 if(h[v] == -1 && edge[pos].w) 56 { 57 h[v] = h[now] + 1; 58 q[ta ++] = v; 59 } 60 } 61 } 62 return h[T] != -1; 63 } 64 65 int dfs(int x, int f) 66 { 67 if(x == T) return f; 68 int w, used = 0; 69 for(int pos = head[x];pos;pos = edge[pos].nxt) 70 { 71 int v = edge[pos].v; 72 if(h[v] == h[x] + 1) 73 { 74 w = dfs(v, min(f - used, edge[pos].w)); 75 edge[pos].w -= w; 76 edge[pos ^ 1].w += w; 77 used += w; 78 if(used == f) return f; 79 } 80 } 81 if(!used) h[x] = -1; 82 return used; 83 } 84 85 void dinic() 86 { 87 while(bfs()) ans += dfs(S, INF); 88 } 89 90 int n,m; 91 92 int main() 93 { 94 S = 301, T = 302; 95 read(n), read(m); 96 for(register int i = 1;i <= n;++ i) 97 { 98 int tmp;read(tmp); 99 if(tmp) insert(S, i, 1); 100 else insert(i, T, 1); 101 } 102 for(register int i = 1;i <= m;++ i) 103 { 104 int tmp1,tmp2; 105 read(tmp1), read(tmp2); 106 insert(tmp1, tmp2, 1); 107 insert(tmp2, tmp1, 1); 108 } 109 dinic(); 110 printf("%d", ans); 111 return 0; 112 }