类似最小乘积生成树的做法
详见另一文:http://blog.csdn.net/u014609452/article/details/51822880
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef pair<int,int> abcd;
inline char nc()
{
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(int &x)
{
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
const int N=75;
int A[N][N],B[N][N];
int n,w[N][N];
int S[N],T[N],mate[N];
int lx[N],ly[N],sla[N];
bool match(int u){
S[u]=1;
for (int v=1;v<=n;v++)
{
if (T[v]) continue;
if (lx[u]+ly[v]-w[u][v]==0)
{
T[v]=1;
if (!mate[v] || match(mate[v]))
return mate[v]=u,1;
}
else
sla[v]=min(sla[v],lx[u]+ly[v]-w[u][v]);
}
return 0;
}
inline abcd KM()
{
cl(mate); cl(ly);
for (int i=1;i<=n;i++)
{
lx[i]=-1<<30;
for (int j=1;j<=n;j++) lx[i]=max(lx[i],w[i][j]);
}
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++) sla[j]=1<<30;
for (;;){
cl(S); cl(T);
if (match(i)) break;
int a=1<<30;
for (int j=1;j<=n;j++) if (!T[j]) a=min(a,sla[j]);
for (int j=1;j<=n;j++) if (S[j]) lx[j]-=a;
for (int j=1;j<=n;j++) if (T[j]) ly[j]+=a; else sla[j]-=a;
}
}
int t1=0,t2=0;
for (int i=1;i<=n;i++)
t1+=A[mate[i]][i],t2+=B[mate[i]][i];
return abcd(t1,t2);
}
int find(abcd l,abcd r)
{
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
w[i][j]=A[i][j]*(r.second-l.second)+B[i][j]*(l.first-r.first);
abcd mid=KM();
if(mid==l || mid==r) return min(r.first*r.second,l.first*l.second);
return min(find(l,mid),find(mid,r));
}
int main()
{
abcd L,R; int Q;
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(Q);
while (Q--)
{
read(n);
for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) read(A[i][j]);
for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) read(B[i][j]);
for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) w[i][j]=-A[i][j];
L=KM();
for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) w[i][j]=-B[i][j];
R=KM();
printf("%d\n",find(L,R));
}
return 0;
}