[CQOI2009]跳舞
因为如果能跳x支舞曲,那么一定能跳y(y < x)支舞曲,满足单调性。因此可二分舞曲。
那么网络流建图就明白了:把每一个男生拆成3个点,分别是男生总,男生喜欢的,男生不喜欢的。然后从源点向男生总连一条容量为x的边,从男生总向男生喜欢的连一条INF的边,向男生不喜欢的连一条k的边。女生同理。
然后对于一对互相喜欢的男女生,就从男生喜欢的向女生喜欢的连一条1的边;对于不喜欢的男女生,就从男生不喜欢的向女生不喜欢的连一条1的边。跑一遍最大流,如果总流量等于x * n,就说明可以跳x支舞曲,向上二分。
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cctype> 8 #include<vector> 9 #include<stack> 10 #include<queue> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(' ') 14 #define Mem(a, x) memset(a, x, sizeof(a)) 15 #define rg register 16 typedef long long ll; 17 typedef double db; 18 const int INF = 0x3f3f3f3f; 19 const db eps = 1e-8; 20 const int maxn = 50; 21 inline ll read() 22 { 23 ll ans = 0; 24 char ch = getchar(), last = ' '; 25 while(!isdigit(ch)) {last = ch; ch = getchar();} 26 while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();} 27 if(last == '-') ans = -ans; 28 return ans; 29 } 30 inline void write(ll x) 31 { 32 if(x < 0) x = -x, putchar('-'); 33 if(x >= 10) write(x / 10); 34 putchar(x % 10 + '0'); 35 } 36 37 int n, k, t; 38 char a[maxn + 5][maxn + 5]; 39 40 struct Edge 41 { 42 int from, to, cap, flow; 43 }; 44 vector<Edge> edges; 45 vector<int> G[maxn * 6 + 5]; 46 void addEdge(int from, int to, int w) 47 { 48 edges.push_back((Edge){from, to, w, 0}); 49 edges.push_back((Edge){to, from, 0, 0}); 50 int sz = edges.size(); 51 G[from].push_back(sz - 2); 52 G[to].push_back(sz - 1); 53 } 54 55 int dis[maxn * 6 + 5]; 56 bool bfs() 57 { 58 Mem(dis, 0); dis[0] = 1; 59 queue<int> q; q.push(0); 60 while(!q.empty()) 61 { 62 int now = q.front(); q.pop(); 63 for(int i = 0; i < (int)G[now].size(); ++i) 64 { 65 Edge& e = edges[G[now][i]]; 66 if(!dis[e.to] && e.cap > e.flow) 67 { 68 dis[e.to] = dis[now] + 1; 69 q.push(e.to); 70 } 71 } 72 } 73 return dis[t]; 74 } 75 int cur[maxn * 6 + 5]; 76 int dfs(int now, int res) 77 { 78 if(now == t || res == 0) return res; 79 int flow = 0, f; 80 for(int& i = cur[now]; i < (int)G[now].size(); ++i) 81 { 82 Edge& e = edges[G[now][i]]; 83 if(dis[e.to] == dis[now] + 1 && (f = dfs(e.to, min(res, e.cap - e.flow))) > 0) 84 { 85 e.flow += f; 86 edges[G[now][i] ^ 1].flow -= f; 87 flow += f; res -= f; 88 if(res == 0) break; 89 } 90 } 91 return flow; 92 } 93 94 int maxflow() 95 { 96 int flow = 0; 97 while(bfs()) 98 { 99 Mem(cur, 0); 100 flow += dfs(0, INF); 101 } 102 return flow; 103 } 104 105 void build_Gra(int x) //暴力重构 106 { 107 edges.clear(); 108 for(int i = 0; i <= t; ++i) G[i].clear(); 109 for(int i = 1; i <= n; ++i) 110 { 111 addEdge(0, i, x); 112 addEdge(i, n + i, INF); 113 addEdge(i, (n << 1) + i, k); 114 addEdge(n * 5 + i, t, x); 115 addEdge((n << 1) + n + i, n * 5 + i, INF); 116 addEdge((n << 2) + i, n * 5 + i, k); 117 for(int j = 1; j <= n; ++j) 118 { 119 if(a[i][j] == 'Y') addEdge(n + i, (n << 1) + n + j, 1); 120 else addEdge((n << 1) + i, (n << 2) + j, 1); 121 } 122 } 123 } 124 125 int main() 126 { 127 n = read(); k = read(); 128 t = (n << 2) + (n << 1) + 1; 129 for(int i = 1; i <= n; ++i) scanf("%s", a[i] + 1); 130 int L = 0, R = n; 131 while(L < R) 132 { 133 int mid = (L + R + 1) >> 1; 134 build_Gra(mid); 135 if(maxflow() == mid * n) L = mid; 136 else R = mid - 1; 137 } 138 write(L); enter; 139 return 0; 140 }