BZOJ3571 : [Hnoi2014]画框

时间:2022-03-23 20:59:01

题目是要求最小乘积最小权匹配,

将一种方案看做一个二维点(x,y),x=a值的和,y=b值的和,所有方案中只有在下凸壳上的点才有可能成为最优解

首先要求出两端的方案l,r两个点

l就是a值的和最小的方案,

r就是b值的和最小的方案

然后递归work(l,r)即可得出所有在下凸壳上的点

work(l,r){

找到一个离lr这条直线最远的且靠近原点的点mid

如果l,mid,r构成下凸壳{

work(l,mid)

work(mid,r)

}

}

如何找mid?

把所有边边权设为(a[i],b[i])它lr直线垂线上的投影,再求普通的最小权匹配即可。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int inf=~0U>>2,N=150,M=100110;
struct P{int x,y;P(){x=y=0;}P(int _x,int _y){x=_x,y=_y;}P operator-(const P&a){return P(x-a.x,y-a.y);}}L,R;
inline int cross(P a,P b){return a.x*b.y-a.y*b.x;}
int n,m,i,j,w[N][N],wa[N][N],wb[N][N],test,ans;
int u[M],v[M],c[M],co[M],coa[M],cob[M],nxt[M],t,S,T,l,r,q[M],g[N],f[N],d[N];bool in[N];
inline void add(int x,int y,int z,int zo,int zoa,int zob){
u[++t]=x;v[t]=y;c[t]=z;co[t]=zo;coa[t]=zoa;cob[t]=zob;nxt[t]=g[x];g[x]=t;
u[++t]=y;v[t]=x;c[t]=0;co[t]=-zo;coa[t]=-zoa;cob[t]=-zob;nxt[t]=g[y];g[y]=t;
}
inline bool spfa(){
memset(d,127,sizeof d);d[S]=0;in[S]=1;l=r=M>>1;q[l]=S;
int x,i;
while(l<=r){
int x=q[l++];
if(x==T)continue;
for(i=g[x];i;i=nxt[i])if(c[i]&&co[i]+d[x]<d[v[i]]){
d[v[i]]=co[i]+d[x];f[v[i]]=i;
if(!in[v[i]]){
in[v[i]]=1;
if(d[v[i]]<d[q[l]])q[--l]=v[i];else q[++r]=v[i];
}
}
in[x]=0;
}
return d[T]<=inf;
}
inline P mincostflow(){
P p;t=1;S=0;T=n*2+1;
for(i=0;i<=T;i++)g[i]=in[i]=0;
for(i=1;i<=n;i++)for(j=1;j<=n;j++)add(i,j+n,1,w[i][j],wa[i][j],wb[i][j]);
for(i=1;i<=n;i++)add(S,i,1,0,0,0),add(i+n,T,1,0,0,0);
while(spfa())for(i=T;i!=S;i=u[f[i]])--c[f[i]],++c[f[i]^1],p.x+=coa[f[i]],p.y+=cob[f[i]];
if(p.x*p.y<ans)ans=p.x*p.y;
return p;
}
void work(P l,P r){
P t=l-r;
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)w[i][j]=cross(P(wa[i][j],wb[i][j]),t);
P mid=mincostflow();
if(cross(mid-l,r-mid)>0)work(l,mid),work(mid,r);
}
int solve(){
ans=~0U>>1;
scanf("%d",&n);
for(i=1;i<=n;i++)for(j=1;j<=n;j++)scanf("%d",&wa[i][j]);
for(i=1;i<=n;i++)for(j=1;j<=n;j++)scanf("%d",&wb[i][j]);
for(i=1;i<=n;i++)for(j=1;j<=n;j++)w[i][j]=wa[i][j];
L=mincostflow();
for(i=1;i<=n;i++)for(j=1;j<=n;j++)w[i][j]=wb[i][j];
R=mincostflow();
work(L,R);
printf("%d\n",ans);
}
int main(){
scanf("%d",&test);
while(test--)solve();
return 0;
}