看到这题,首先看到,一对一,那么感觉是二分图?看了看数据,挺像的。
然后看到,原来是要一个C值最大。很典型的分数规划。我们转移一下就可以得到(a_1 - b_1 * c) + (a_2 - b_2 * c) + (a_3 - b_3 * c) + (a_4 - b_4 * c)....... = 0
所以我们只要去枚举c,然后不断让上式向着零趋近,最后输出即可。
当然,我们肯定不会去枚举的,先定一个区间,然后不断枚举c即可。
这个是一对一匹配,每条边是有权值的,如果想上二分图,就写KM算法。然后我并不会。。。所以用费用流代替是一个很好的选择。因为他是加法,所以我们应该是跑最长路,相当于把到达每个点的费用都累加起来,最后在汇点就得到了总共的费用了。
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define eps 1e-10
const int maxn = 205;
const int maxm = 150 * 150 * 2;
const int INF = 0x3f3f3f3f;
int head[maxn], to[maxm], front[maxm], flow[maxm], ppp;
double cost[maxm], dis[maxn];
bool flag[maxn];
int minflow[maxn];
int n;
pair<int, int>par[maxn];
bool spfa(int s, int e)
{
int u, v;
for(int i = s; i <= e; i++)
dis[i] = -INF;
memset(flag, 0, sizeof(flag));
dis[s] = 0;
minflow[s] = INF;
queue <int> q;
q.push(s);
while(!q.empty())
{
u = q.front();
q.pop();
flag[u] = 0;
for(int i = head[u]; ~i; i = front[i])
{
v = to[i];
if(flow[i] && dis[v] < dis[u] + cost[i])
{
dis[v] = dis[u] + cost[i];
par[v] = (make_pair(u, i));
minflow[v] = min(minflow[u], flow[i]);
if(!flag[v])
{
flag[v] = 1;
q.push(v);
}
}
}
}
return dis[e] > -INF;
}
double Min_Cost_Max_Flow(int s, int e)
{
double ans = 0;
int p;
while(spfa(s, e))
{
p = e;
while(p != s)
{
flow[par[p].second] -= minflow[e];
flow[par[p].second^1] += minflow[e];
p = par[p].first;
}
ans += dis[e];
}
return ans;
}
void add_edge(int u, int v, int f, double c)
{
to[ppp] = v;
front[ppp] = head[u];
flow[ppp] = f;
cost[ppp] = c;
head[u] = ppp++;
}
double a[maxn][maxn], b[maxn][maxn];
void init(double c) {
memset(head, -1, sizeof(head));
ppp = 0;
for(int i = 1; i <= n; i++) {
add_edge(0, i, 1, 0);
add_edge(i, 0, 0, 0);
add_edge(n + i, 2 * n + 1, 1, 0);
add_edge(2 * n + 1, n + i, 0, 0);
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
add_edge(i, n + j, 1, a[i][j] - b[i][j] * c);
add_edge(n + j, i, 0, -(a[i][j] - b[i][j] * c));
}
}
}
int main() {
// freopen("in.txt", "r", stdin);
cin >> n;
for(int i = 1; i <=n; i++) {
for(int j = 1; j <= n; j++) {
cin >> a[i][j];
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
cin >> b[i][j];
}
}
double front = 0, back = 1e4;
for(int i = 0; i < 100; i++) {
double c = (front + back) / 2.0;
init(c);
double tmp = Min_Cost_Max_Flow(0, 2 * n + 1);
if(tmp <= 0) {
back = c;
} else {
front = c;
}
}
printf("%6f\n", front);
return 0;
}