题目链接:[HNOI2014]画框
最小乘积生成树的近似做法QAQ
对于普通的最小乘积生成树,我们都是有两个点权ai,bi求生成树是的sigma(ai)*sigma(bi)最小
现在我们可以把生成树看成是完备匹配,然后就和最小乘积生成树一样做了
关于最小乘积生成树怎么做已经烂大街了,大家自行去找即可QAQ
#include<cstdio>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=100;
int tim=0,n,mat[maxn];
int a[maxn][maxn],b[maxn][maxn],w[maxn][maxn];
struct point{int x,y;};
int lx[maxn],ly[maxn],sta[maxn];
bool visx[maxn],visy[maxn];
bool find(int x){
visx[x]=1;
for (int y=1;y<=n;++y)
if (!visy[y]){
int tmp=lx[x]+ly[y]-w[x][y];
if (!tmp){
visy[y]=1;
if (!mat[y]||find(mat[y])){
mat[y]=x;return 1;}
}else sta[y]=min(tmp,sta[y]);//!!!!!
}return 0;
}
point KM(){
memset(lx,0,sizeof(lx));
memset(ly,0,sizeof(ly));
memset(mat,0,sizeof(mat));
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
lx[i]=max(lx[i],w[i][j]);
for (int x=1;x<=n;++x){
memset(sta,127/3,sizeof(sta));
while (true){
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
if (find(x)) break;
int d=0x7fffffff/3;
for (int i=1;i<=n;++i)
if (!visy[i]) d=min(d,sta[i]);///!!!
for (int i=1;i<=n;++i){
if (visx[i]) lx[i]-=d;
if (visy[i]) ly[i]+=d;
}
}
}point ret; ret.x=0; ret.y=0;
for (int i=1;i<=n;++i)
ret.x+=a[mat[i]][i],ret.y+=b[mat[i]][i];
return ret;
}
int solve_MST(point l,point r){
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
w[i][j]=a[i][j]*(r.y-l.y)+b[i][j]*(l.x-r.x);
point tmp=KM();
if ((l.x==tmp.x&&l.y==tmp.y)||(r.x==tmp.x&&r.y==tmp.y))
return min(l.x*l.y,r.x*r.y);
else return min(solve_MST(l,tmp),solve_MST(tmp,r));
}
int main(){
int T; scanf("%d",&T);
for (int i=1;i<=T;++i){
scanf("%d",&n);
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
scanf("%d",&a[i][j]);
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
scanf("%d",&b[i][j]);
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
w[i][j]=-a[i][j];
point A=KM();
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
w[i][j]=-b[i][j];
point B=KM();
int ans=solve_MST(A,B);
printf("%d\n",ans);
}
}