这道题考试的时候我直接写了个KM拿30分就滚去写下一题了orz。。
看了题解感觉想出来的人脑洞真的…………比较大………………
把每一种匹配 (sigma(A), sigma(B))看做平面上的一个点
因为要求乘积最小 可以证明这个点肯定在下凸壳上 于是把下凸壳上的每个点逐一验证就可以了
构造下凸壳用的分治_(:з)∠)_
首先纵坐标最大的和横坐标最小的肯定在下凸壳上
然后找到一个点距离它们连成的直线距离最远 这个点肯定也在下凸壳上
然后再把这个点和原来的两个点构成新的直线再找最远的点直到找不到就行了
找最远的时候拿数学公式YY一下就行了 _(:з)∠)_
10组数据4000多ms感觉基本是卡过去的。。不知道是不是因为pair有常数?
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int read()
{
int n = 0, sign = 1; char c = getchar();
while(c > '9' || c < '0') {if(c == '-') sign = -1; c = getchar();}
while(c >= '0' && c <= '9') {n = n*10 + c-'0'; c = getchar(); }
return sign * n;
}
typedef pair<int, int> point;
const int inf = 0x3f3f3f3f;
int T, N;
int A[75][75], B[75][75], w[75][75], match[75];
int lx[75], ly[75], slack[75];
bool visx[75], visy[75];
bool dfs(int u)
{
visx[u] = true;
for(int v = 1; v <= N; ++v)
{
if(visy[v] == true) continue;
int t = lx[u] + ly[v] - w[u][v];
if(t == 0)
{
visy[v] = true;
if(match[v] == -1 || dfs(match[v]))
{
match[v] = u;
return true;
}
}
else if(slack[v] > t) slack[v] = t;
}
return false;
}
inline void adjust()
{
int d = inf;
for(int i = 1; i <= N; ++i) if(!visy[i] && d > slack[i]) d = slack[i];
for(int i = 1; i <= N; ++i) if(visx[i]) lx[i] -= d;
for(int i = 1; i <= N; ++i) if(visy[i]) ly[i] += d; else slack[i] -= d;
}
inline point KM()
{
memset(match, -1, sizeof(match));
memset(ly, 0, sizeof(ly));
for(int i = 1; i <= N; ++i)
{
lx[i] = -inf;
for(int j = 1; j <= N; ++j) if(w[i][j] > lx[i]) lx[i] = w[i][j];
}
for(int i = 1; i <= N; ++i)
{
for(int j = 1; j <= N; ++j) slack[j] = inf;
for( ; ; )
{
memset(visx, 0, sizeof(visx));
memset(visy, 0, sizeof(visy));
if(dfs(i)) break;
else adjust();
}
}
int temp1 = 0, temp2 = 0;
for(int i = 1; i <= N; ++i) if(match[i] != -1)
{
temp1 += A[match[i]][i];
temp2 += B[match[i]][i];
}
return point(temp1, temp2);
}
int find(point P, point Q)
{
for(int i = 1; i <= N; ++i)
for(int j = 1; j <= N; ++j)
w[i][j] = A[i][j] * (Q.second - P.second) + B[i][j] * (P.first - Q.first);
point temp = KM();
if(temp == P || temp == Q) return min(P.first * P.second, Q.first * Q.second);
return min(find(P, temp), find(temp, Q));
}
int main()
{
freopen("frame.in", "r", stdin);
freopen("frame.out", "w", stdout);
for(T = read(); T--; )
{
N = read();
for(int i = 1; i <= N; ++i)
for(int j = 1; j <= N; ++j)
{ A[i][j] = read(); w[i][j] = -A[i][j]; }
for(int i = 1; i <= N; ++i)
for(int j = 1; j <= N; ++j)
B[i][j] = read();
point temp1 = KM();
for(int i = 1; i <= N; ++i)
for(int j = 1; j <= N; ++j)
w[i][j] = B[i][j];
point temp2 = KM();
printf("%d\n", find(temp1, temp2));
}
return 0;
}