[CQOI2009]跳舞 网络流
题解:
首先最大时间不好求,而且数据范围很小,所以我们可以先二分一个最大时间,然后就只需要判断是否可行即可。
因此我们每二分一个mid,对于每个女生,连s ---> x : mid , x ---> x' : k.对于每个男生,连x ---> t : mid, x' ---> x : k.
对于每条边,如果为Y,连x ---> y.否则连x' ---> y'.
其中x和x'分别表示一个人和这个人拆分出来的点。
为什么这么连?
对于每个女生,连s ---> x : mid , x ---> x' : k。 其中s ---> x : mid 表示一共要进行mid次,x ---> x' : k表示这mid次中最多可以选k个和不喜欢的人相连。
对于每个男生,连x ---> t : mid, x' ---> x : k. 同上。
对于每条边,如果为Y,连x ---> y.否则连x' ---> y'. 如果是Y,说明互相喜欢,所以用原本的点相连,否则说明互相不喜欢,就用拆分出的点相连。因为拆分出的点最多只有k次机会,因此表示的是连向不喜欢的点。
我一开始是这么想的,但是为了看上去简单一点,对于每个女生,我连了s --- > x : lim - k, s --- > x' : k.
这样是不对的,因为这样就固定了喜欢的人也最多选lim - k个,而实际上是没有这个限制的。因此我们用x ---> x' : k的方法就可以去掉这个限制,并且可以让它相对动态的分配每次机会,而不是每次固定的只能选lim - k次喜欢的,和强制选k次不喜欢的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 400 5 #define ac 40000 6 #define inf 1000000000 7 8 int n, m, ans, all, sum, s, t, head, tail, x, addflow, k; 9 int Head[AC], Next[ac], date[ac], haveflow[ac], tot = 1; 10 int have[AC], c[AC], good[AC], last[AC], q[ac]; 11 char f[AC][AC]; 12 13 inline int read() 14 { 15 int x = 0;char c = getchar(); 16 while(c > '9' || c < '0') c = getchar(); 17 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 18 return x; 19 } 20 21 void init() 22 { 23 memset(Head, 0, sizeof(Head)); 24 memset(have, 0, sizeof(have)); 25 memset(c, 0, sizeof(c)); 26 ans = 0, tot = 1; 27 } 28 29 inline void add(int f, int w, int S) 30 { 31 date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot, haveflow[tot] = S; 32 date[++ tot] = f, Next[tot] = Head[w], Head[w] = tot, haveflow[tot] = 0; 33 //printf("%d %d : %d\n", f, w, S); 34 } 35 36 inline void upmin(int &a, int b){ 37 if(b < a) a = b; 38 } 39 40 void build(int lim) 41 { 42 sum = lim * m; 43 for(R i = 1; i <= n; i ++) add(s, i, lim), add(i, n + i, k); 44 for(R i = 1; i <= n; i ++) 45 for(R j = 1; j <= m; j ++) 46 { 47 if(f[i][j] == 'Y') add(i, n + n + j, 1); 48 else add(n + i, n + n + m + j, 1); 49 } 50 for(R i = 1; i <= m; i ++) add(n + n + i, t, lim), add(n + n + m + i, n + n + i, k); 51 } 52 53 void bfs() 54 { 55 head = tail = 0; 56 q[++ tail] = t, c[t] = 1, have[1] = 1; 57 while(head < tail) 58 { 59 int x = q[++ head]; 60 for(R i = Head[x]; i; i = Next[i]) 61 { 62 int now = date[i]; 63 if(!c[now] && haveflow[i ^ 1]) 64 ++ have[c[now] = c[x] + 1], q[++ tail] = now; 65 } 66 } 67 memcpy(good, Head, sizeof(Head)); 68 } 69 70 void aru() 71 { 72 while(x != s) 73 { 74 haveflow[last[x]] -= addflow; 75 haveflow[last[x] ^ 1] += addflow; 76 x = date[last[x] ^ 1]; 77 } 78 ans += addflow; 79 } 80 81 void isap() 82 { 83 bool done = false; 84 x = s, addflow = inf; 85 while(c[s] != all) 86 { 87 if(x == t) aru(), addflow = inf; 88 done = false; 89 for(R i = good[x]; i; i = Next[i]) 90 { 91 int now = date[i]; 92 good[x] = i; 93 if(c[now] == c[x] - 1 && haveflow[i]) 94 { 95 done = true, x = now, last[now] = i; 96 upmin(addflow, haveflow[i]); 97 break; 98 } 99 } 100 if(!done) 101 { 102 int go = all - 1; 103 for(R i = Head[x]; i; i = Next[i]) 104 if(c[date[i]] && haveflow[i]) upmin(go, c[date[i]]); 105 if(!(-- have[c[x]])) break; 106 ++ have[c[x] = go + 1], good[x] = Head[x]; 107 if(x != s) x = date[last[x] ^ 1]; 108 } 109 } 110 } 111 112 bool check(int lim) 113 { 114 init(), build(lim), bfs(), isap(); 115 if(ans == sum) return true; 116 else return false; 117 } 118 119 void half() 120 { 121 int l = 0, r = n, mid; 122 while(l < r) 123 { 124 mid = (l + r + 1) >> 1; 125 if(check(mid)) l = mid; 126 else r = mid - 1; 127 } 128 printf("%d\n", l); 129 } 130 131 void pre() 132 { 133 n = read(), m = n, k = read(); 134 s = n + n + m + m + 1, t = s + 1, all = t + 5; 135 for(R i = 1; i <= n; i ++) scanf("%s", f[i] + 1); 136 } 137 138 int main() 139 { 140 freopen("in.in", "r", stdin); 141 pre(); 142 half(); 143 fclose(stdin); 144 return 0; 145 } 146