1934: [Shoi2007]Vote 善意的投票
Time Limit: 1 Sec Memory Limit: 64 MBSubmit: 2489 Solved: 1544
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
1 0 0
1 2
1 3
3 2
Sample Output
HINT
在第一个例子中,所有小朋友都投赞成票就能得到最优解
Source
【题解】
我直接说了,如果说错了还请见谅
网上的绝大多数(99%)题解对本题建模方式采用双向边的解释都是错误的
本题这样建图:
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的一条路径,不是割
这样割边就意味着改变了一个点的立场,少割了跟它不同立场的朋友,多割了跟它同立场的朋友
这才是原题的意思
这才是加双向边的原因
已经不是第一次遇到这种网上题解几乎全军覆没的情况了
不知道是我理解不了大家的见解,还是有些人人云亦云,不求甚解
如果各位不理解,还请评论区留言
如果是我错了,还请各位不吝赐教
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 }