$ color{#0066ff}{ 题目描述 }$
学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴。
有(n)个男生和(n)个女生参加舞会买一个男生和一个女生一起跳舞,互为舞伴。
Cathy收集了这些同学之间的关系,比如两个人之前认识没计算得出 (a_{i,j})
Cathy还需要考虑两个人一起跳舞是否方便,比如身高体重差别会不会太大,计算得出 (b_{i,j}),表示第i个男生和第j个女生一起跳舞时的不协调程度。
当然,还需要考虑很多其他问题。
Cathy想先用一个程序通过(a_{i,j})和(b_{i,j}),j求出一种方案,再手动对方案进行微调。
Cathy找到你,希望你帮她写那个程序。
一个方案中有n对舞伴,假设没对舞伴的喜悦程度分别是(a'_1,a'_2,...,a'_n),假设每对舞伴的不协调程度分别是(b'_1,b'_2,...,b'_n)。令
(C=frac{a'_1+a'_2+...+a'_n}{b'_1+b'_2+...+b'_n})
Cathy希望C值最大。
(color{#0066ff}{输入格式})
第一行一个整数n。
接下来n行,每行n个整数,第i行第j个数表示(a_{i,j})。
接下来n行,每行n个整数,第i行第j个数表示(b_{i,j})。
(color{#0066ff}{输出格式})
一行一个数,表示C的最大值。四舍五入保留6位小数,选手输出的小数需要与标准输出相等。
(color{#0066ff}{输入样例})
3
19 17 16
25 24 23
35 36 31
9 5 6
3 4 2
7 8 9
(color{#0066ff}{输出样例})
5.357143
(color{#0066ff}{数据范围与提示})
对于10%的数据,(1le nle 5)
对于40%的数据,(1le nle 18)
另有20%的数据,(b_{i,j}le 1)
对于100%的数据,(1le nle 100,1le a_{i,j},b_{i,j}<=10^4)
(color{#0066ff}{题解})
很显然这是个01分数规划, 二分答案就行
考虑二分完之后咋整,要是新权值和最小(大), 二人一一对应, 用费用流就行了
注意二分范围
#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
const double eps = 1e-8;
const int maxn = 1e5 + 100;
const int maxm = 1020;
const int inf = 0x7fffffff;
int n, s, t;
struct node {
int to, can;
double dis;
node *nxt, *rev;
node(int to = 0, int can = 0, double dis = 0, node *nxt = NULL): to(to), can(can), dis(dis), nxt(nxt) { rev = NULL; }
}pool[maxn], *tail;
node *head[maxm];
bool vis[maxm];
double dis[maxm];
int a[maxm][maxm], b[maxm][maxm];
void init() {
for(int i = s; i <= t; i++) head[i] = NULL;
tail = pool;
}
void add(int from, int to, int can, double dis) {
head[from] = new(tail++) node(to, can, dis, head[from]);
}
void link(int from, int to, int can, double dis) {
add(from, to, can, dis), add(to, from, 0, -dis);
(head[from]->rev = head[to])->rev = head[from];
}
bool spfa() {
for(int i = s; i <= t; i++) vis[i] = false, dis[i] = 1e15;
std::deque<int> q;
dis[t] = 0;
q.push_back(t);
while(!q.empty()) {
int tp = q.front(); q.pop_front();
vis[tp] = false;
for(node *i = head[tp]; i; i = i->nxt) {
if(dis[i->to] - (dis[tp] - i->dis) > eps && i->rev->can) {
dis[i->to] = dis[tp] - i->dis;
if(!vis[i->to]) {
vis[i->to] = true;
if(!q.empty() && dis[q.front()] - dis[i->to] > eps) q.push_front(i->to);
else q.push_back(i->to);
}
}
}
}
return 1e15 - dis[s] > eps;
}
int dfs(int x, int change) {
if(x == t || !change) return change;
vis[x] = true;
int flow = 0, ls;
for(node *i = head[x]; i; i = i->nxt) {
if(!vis[i->to] && fabs(dis[i->to] - (dis[x] - i->dis)) <= eps && (ls = dfs(i->to, std::min(change, i->can)))) {
change -= ls;
flow += ls;
i->rev->can += ls;
i->can -= ls;
if(!change) break;
}
}
return flow;
}
double zkw() {
double cost = 0;
while(spfa()) {
vis[t] = true;
while(vis[t]) {
for(int i = s; i <= t; i++) vis[i] = false;
cost += 1.0 * dfs(s, inf) * dis[s];
}
}
return cost;
}
bool ok(double mid) {
s = 0, t = 2 * n + 1;
init();
for(int i = 1; i <= n; i++) link(s, i, 1, 0), link(i + n, t, 1, 0);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
link(i, j + n, 1, (double)(mid * b[i][j] - 1.0 * a[i][j]));
return zkw() <= -eps;
}
int main() {
n = in();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
a[i][j] = in();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
b[i][j] = in();
double l = 0, r = 1e5, ans = 0;
while(r - l >= eps) {
double mid = (l + r) / 2.0;
if(ok(mid)) ans = mid, l = mid;
else r = mid;
}
printf("%.6f
", ans);
return 0;
}