就类似最小乘积生成树,在凸壳上面分治就完了
就是把kruscal改成KM
Code:
#include<bits/stdc++.h>
#define pb push_back
#define ll long long
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
const int N=75,INF=1e9;
int n,a[N][N],b[N][N],ans;
struct point{
int x,y;
point(){}
point(int _x,int _y):x(_x),y(_y){}
friend inline point operator - (const point &a,const point &b){return point(a.x-b.x,a.y-b.y);}
friend inline int operator * (const point &a,const point &b){return a.x*b.y-a.y*b.x;}
};
namespace KM{
int lx[N],ly[N],ptx[N],pty[N],g[N][N],match[N];
bool hungary(int x){
ptx[x]=1;
for(int y=1;y<=n;y++){
if(!pty[y] && lx[x]+ly[y]==g[x][y]){
pty[y]=1;
if(!match[y] || hungary(match[y])) {match[y]=x;return 1;};
}
}
return false;
}
inline point km(int xx,int yy){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
g[i][j]=-(xx*a[i][j]+yy*b[i][j]);
for(int i=1;i<=n;i++){
ly[i]=0;int tmp=0;
for(int j=1;j<=n;j++) tmp=max(tmp,g[i][j]);
lx[i]=tmp;
}
memset(match,0,sizeof(match));
for(int x=1;x<=n;x++)
while(1){
memset(ptx,0,sizeof(ptx));
memset(pty,0,sizeof(pty));
if(hungary(x)) break;
int tmp=INF;
for(int i=1;i<=n;i++) if(ptx[i])
for(int j=1;j<=n;j++) if(!pty[j])
tmp=min(tmp,lx[i]+ly[j]-g[i][j]);
for(int i=1;i<=n;i++) if(ptx[i]) lx[i]-=tmp;
for(int i=1;i<=n;i++) if(pty[i]) ly[i]+=tmp;
}
point ans=point(0,0);
for(int i=1;i<=n;i++) ans.x+=a[match[i]][i],ans.y+=b[match[i]][i];
return ans;
}
}
void solve(point A,point B){
point C=KM::km(A.y-B.y,B.x-A.x);
ans=min(ans,C.x*C.y);
if((B-A)*(C-A)>=0) return;
solve(A,C);solve(C,B);
}
int main(){
int t=read();
while(t--){
n=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) a[i][j]=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) b[i][j]=read();
point A=KM::km(1,0);
point B=KM::km(0,1);
ans=min(A.x*A.y,B.x*B.y);
solve(A,B);cout<<ans<<"\n";
}
return 0;
}