https://www.luogu.com.cn/problem/P3705
挺套路一题......
把他想象成一个(n imes n)的矩阵,然后每行每列只能选一个数,让你最大化选出的(a)的和与(b)的和的商,也就是
[C = frac{sum a}{sum b}
]
然后就是非常套路的分数规划.....
先二分一个(mid),那么
[frac{sum a}{sum b} ge mid
]
[sum a ge midsum b
]
[sum a-mid imes b ge 0
]
然后把位置((i,j))的权值改成(a_{i,j}-mid*b_{i,j})。
问题变为在这个矩阵中,每行每列只能选一个数,问选出来的数的和最大是多少。
建一张二分图,(X)节点表示行,(Y)节点表示列,对于(a_{i,j})(((i,j))的权值),连一条(X_i ightarrow Y_j),权值为(a_{i,j})的边。
然后判断这张图的最大全完美匹配是不是大于(0)就好了。
不会KM ==...然后就写了又长又慢的费用流...
另外二分的bound不要瞎开...容易T...
#include <bits/stdc++.h>
using namespace std;
typedef double db;
const db INF = 1e9;
template <typename cap_t, typename cost_t>
struct MCMF {
static const int E = 1e5 + 10, V = 1e5 + 10;
struct edge {
int from, to, nxt;
cap_t cap, flow;
cost_t cost;
} e[E << 1];
int fst[V], n, cnt, s, t;
void init(int S, int T, int sz) {
n = sz, cnt = 1;
s = S, t = T;
for (int i = 0; i <= n; i++) fst[i] = 0;
}
void ade(int u, int v, cap_t w, cost_t c) {
e[++cnt] = {u, v, fst[u], w, 0, c};
fst[u] = cnt;
}
void addedge(int u, int v, cap_t w, cost_t c) {
ade(u, v, w, c), ade(v, u, 0, -c);
}
cap_t incf[V]; cost_t d[V];
int pre[V]; bool vis[V];
bool spfa() {
for (int i = 0; i <= n; i++) {
incf[i] = (cap_t) INF, d[i] = (cost_t) INF;
vis[i] = 0, pre[i] = 0;
}
d[s] = 0, vis[s] = 1;
vector<int> q;
q.push_back(s);
for (int cur = 0; cur < q.size(); cur++) {
int u = q[cur];
vis[u] = 0;
for (int i = fst[u]; i; i = e[i].nxt) {
int v = e[i].to; cost_t w = e[i].cost;
if (d[v] > d[u] + w && e[i].cap > e[i].flow) {
d[v] = d[u] + w;
pre[v] = i;
incf[v] = min(e[i].cap - e[i].flow, incf[u]);
if (!vis[v]) vis[v] = 1, q.push_back(v);
}
}
}
return d[t] < (cost_t) INF;
}
cap_t maxflow; cost_t mincost;
void augment() {
int u = t;
for (; u != s; u = e[pre[u]].from) {
e[pre[u]].flow += incf[t];
e[pre[u]^1].flow -= incf[t];
}
maxflow += incf[t], mincost += incf[t] * d[t];
}
pair<cap_t, cost_t> mcmf() {
maxflow = mincost = 0;
while (spfa()) augment();
return make_pair(maxflow, mincost);
}
};
MCMF<int, db> g;
const db eps = 1e-7;
const int N = 110;
int a[N][N], b[N][N], n;
bool chk(db mid) {
int s = 0, t = n*2 + 1;
g.init(s, t, n*2 + 2);
for (int i = 1; i <= n; i++) g.addedge(s, i, 1, 0);
for (int i = 1; i <= n; i++) g.addedge(i + n, t, 1, 0);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) g.addedge(i, j+n, 1, b[i][j]*mid - a[i][j]);
auto ans = g.mcmf();
return -ans.second >= 0;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) scanf("%d", &a[i][j]);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) scanf("%d", &b[i][j]);
db l = 0, r = 1e4;
while (l + eps < r) {
db mid = (l+r) / 2;
if (chk(mid)) l = mid;
else r = mid;
}
printf("%.6f
", l);
return 0;
}