bzoj4443
二分+二分图匹配
晚上脑子不太好使。。。
行列模型,填充数量性质,种种迹象告诉我们这是二分图,但是我觉得好像不太科学就弃了网络流。。。
二分第k大值,转化为求第n-k+1小值,二分求匹配判定即可。
#include<bits/stdc++.h> using namespace std; const int N = 810, inf = 1000000010; namespace IO { const int Maxlen = N; char buf[Maxlen], *C = buf; int Len; inline void read_in() { Len = fread(C, 1, Maxlen, stdin); buf[Len] = '\0'; } inline void fread(int &x) { x = 0; int f = 1; while (*C < '0' || '9' < *C) { if(*C == '-') f = -1; ++C; } while ('0' <= *C && *C <= '9') x = (x << 1) + (x << 3) + *C - '0', ++C; x *= f; } inline void read(int &x) { x = 0; int f = 1; char c = getchar(); while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9') { x = (x << 1) + (x << 3) + c - '0'; c = getchar(); } x *= f; } inline void read(long long &x) { x = 0; long long f = 1; char c = getchar(); while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9') { x = (x << 1ll) + (x << 3ll) + c - '0'; c = getchar(); } x *= f; } } using namespace IO; struct edge { int nxt, to, f; } e[N * N * 10]; int n, m, cnt = 1, source, sink, k; int head[N], d[N], a[N][N], iter[N]; inline void link(int u, int v, int f) { e[++cnt].nxt = head[u]; head[u] = cnt; e[cnt].to = v; e[cnt].f = f; } inline void insert(int u, int v, int f) { link(u, v, f); link(v, u, 0); } inline bool bfs() { queue<int> q; memset(d, -1, sizeof(d)); d[source] = 0; q.push(source); while(!q.empty()) { int u = q.front(); q.pop(); for(int i = head[u]; i; i = e[i].nxt) if(e[i].f && d[e[i].to] == -1) { d[e[i].to] = d[u] + 1; q.push(e[i].to); } } return d[sink] != -1; } inline int dfs(int u, int delta) { if(u == sink) return delta; int ret = 0; for(int &i = iter[u]; i && delta; i = e[i].nxt) if(e[i].f && d[e[i].to] == d[u] + 1) { int flow = dfs(e[i].to, min(e[i].f, delta)); e[i].f -= flow; e[i ^ 1].f += flow; delta -= flow; ret += flow; } return ret; } inline int dinic() { int ret = 0; while(bfs()) { for(int i = source; i <= sink; ++i) iter[i] = head[i]; ret += dfs(source, inf); } return ret; } inline bool check(int mid) { memset(head, 0, sizeof(head)); cnt = 1; for(int i = 1; i <= n; ++i) { insert(source, i, 1); for(int j = 1; j <= m; ++j) if(a[i][j] <= mid) insert(i, j + n, 1); } for(int i = 1; i <= m; ++i) insert(i + n, i + m + n, 1), insert(i + m + n, sink, 1); int ret = dinic(); return ret >= n - k + 1; } int main() { read(n); read(m); read(k); sink = 2 * m + n + 1; for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) read(a[i][j]); int l = 0, r = inf + 10, ans = 0; while(r - l > 1) { int mid = (l + r) >> 1; if(check(mid)) r = ans = mid; else l = mid; } printf("%d\n", ans); return 0; }