【SDOI2017】新生舞會
阿新 • • 發佈:2019-01-14
題面
題解
一眼\(0/1\)分數規劃
二分答案\(mid\),我們要\(\sum\limits_i a^{'}_i - mid\sum\limits_i b_i^{'}\)最大
那麼我們將\(a_{i,j}-mid\times b_{i,j}\)作為\((i,j)\)的邊權
跑一遍二分圖最大權匹配即可。
程式碼
// luogu-judger-enable-o2 #include<cstdio> #include<cstring> #include<cctype> #include<cmath> #include<algorithm> #include<queue> #define RG register #define file(x) freopen(#x".in", "r", stdin);freopen(#x".out", "w", stdout); #define clear(x, y) memset(x, y, sizeof(x)) inline int read() { int data = 0, w = 1; char ch = getchar(); while(ch != '-' && (!isdigit(ch))) ch = getchar(); if(ch == '-') w = -1, ch = getchar(); while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar(); return data * w; } const int N(110), maxn(5e5 + 10); const double eps(1e-8); struct edge { int next, to, cap; double dis; } e[maxn]; int head[maxn], e_num = -1, S, T, a[N][N], b[N][N]; int pre[maxn], pre_e[maxn], vis[maxn], n; double dis[maxn], cost; inline void add_edge(int from, int to, int cap, double dis) { e[++e_num] = (edge) {head[from], to, cap, dis}; head[from] = e_num; e[++e_num] = (edge) {head[to], from, 0, -dis}; head[to] = e_num; } std::queue<int> q; void MinCostMaxFlow() { cost = 0; while(1) { std::fill(dis + S, dis + T + 1, -1e20); clear(vis, 0); vis[S] = 1, dis[S] = 0, q.push(S); while(!q.empty()) { int x = q.front(); q.pop(); for(RG int i = head[x]; ~i; i = e[i].next) { int to = e[i].to; double ds = e[i].dis + dis[x]; if(e[i].cap > 0 && dis[to] < ds) { dis[to] = ds, pre[to] = x, pre_e[to] = i; if(!vis[to]) vis[to] = 1, q.push(to); } } vis[x] = 0; } if(dis[T] == -1e20) return; int cap = 1e9; for(RG int i = T; i ^ S; i = pre[i]) cap = std::min(cap, e[pre_e[i]].cap); cost += 1. * cap * dis[T]; for(RG int i = T; i ^ S; i = pre[i]) e[pre_e[i]].cap -= cap, e[pre_e[i] ^ 1].cap += cap; } } bool check(double mid) { e_num = -1, S = 1, T = (n << 1) + 2; for(RG int i = S; i <= T; i++) head[i] = -1; for(RG int i = 1; i <= n; i++) add_edge(S, i + 1, 1, 0); for(RG int i = n + 2; i < T; i++) add_edge(i, T, 1, 0); for(RG int i = 1; i <= n; i++) for(RG int j = 1; j <= n; j++) add_edge(i + 1, j + n + 1, 1, 1. * a[i][j] - 1. * b[i][j] * mid); MinCostMaxFlow(); return fabs(cost) <= eps || cost > eps; } int main() { n = read(); for(RG int i = 1; i <= n; i++) for(RG int j = 1; j <= n; j++) a[i][j] = read(); for(RG int i = 1; i <= n; i++) for(RG int j = 1; j <= n; j++) b[i][j] = read(); double l = -1e7, r = 1e7; while(fabs(r - l) > eps) { double mid = (l + r) / 2; if(check(mid)) l = mid; else r = mid; } printf("%.6lf\n", r); return 0; }