這道題太明顯了,一眼看過去就知道是0/1分數規劃。
先用二分枚舉mid,假設mid就是c
我們要判斷的是
然後我們就可以把分母乘到c上面去,得到
這樣以後化簡完不就沒得做了嗎?
如果做過很多道0/1分數規劃的題一定會知道用圖論來求,那麼我們就可以把每一項a和b放在一起,得到
然後我們把男生和女生之間都連一條邊,這條邊流量爲1,費用爲,用最大費用最大流跑一遍,看看最大的費用是否大於0即可
參考代碼
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <queue>
#define inf 0x3f3f3f3f
#define eps 1e-8
using namespace std;
const int N = 2e2 + 6, M = 2e5 + 6;
int tot, Head[N<<1], ver[M<<1], W[M<<1], cur[N<<1], Next[M<<1]; double Leng[M<<1];
int mark[N<<1], vis[N<<1], S, T, n;
double dis[N<<1], ans, a[N][N], b[N][N];
void add(int x, int y, int w, double d) {
ver[++tot] = y, W[tot] = w, Leng[tot] = d, Next[tot] = Head[x]; Head[x] = tot;
ver[++tot] = x, W[tot] = 0, Leng[tot] = -d, Next[tot] = Head[y]; Head[y] = tot;
}
bool SPFA() {
for (int i = S; i <= T; i++) dis[i] = -inf;
memset(vis, 0, sizeof(vis)); queue<int> q;
q.push(S); dis[S] = 0;
while (!q.empty()) {
int u = q.front(); q.pop(); vis[u] = 0;
for (int i = Head[u]; i; i = Next[i]) {
int v = ver[i];
if (W[i] && dis[v] < dis[u] + Leng[i]) {
dis[v] = dis[u] + Leng[i];
if (!vis[v]) q.push(v), vis[v] = 1;
}
}
}
return dis[T] != -inf;
}
int dfs(int x, int now) {
mark[x] = 1;
if (x == T) return now;
int val = 0;
for (int &i = cur[x], t; i; i = Next[i]) {
int v = ver[i];
if (!mark[v] && W[i] && dis[v] == dis[x] + Leng[i]) {
t = dfs(v, min(now, W[i]));
if (t > 0) {
W[i] -= t, W[i ^ 1] += t;
val += t, now -= t;
ans += t * Leng[i];
if (now == 0) break;
}
}
}
return val;
}
void ZKW() {
ans = 0;
while (SPFA()) {
memcpy(cur, Head, sizeof(Head));
mark[T] = 1;
while (mark[T]) {
memset(mark, 0, sizeof(mark));
dfs(S, inf);
}
}
}
bool check(double x) {
S = 0, T = 2 * n + 1; tot = 1;
memset(Head, 0, sizeof(Head));
for (int i = 1; i <= n; i++)
add(S, i, 1, 0), add(i + n, T, 1, 0);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
add(i, j + n, 1, a[i][j] - x * b[i][j]);
ZKW();
return ans >= 0.0;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
scanf("%lf", &a[i][j]);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
scanf("%lf", &b[i][j]);
double l = 0.0, r = 10006.0, mid, Ans;
while (r - l > eps) {
mid = (l + r) / 2.0;
if (check(mid)) Ans = mid, l = mid + eps;
else r = mid - eps;
}
printf("%.6lf\n", Ans);
return 0;
}