您的位置:首页 > 其它

bzoj3571 [Hnoi2014]画框

2017-07-29 12:06 344 查看

Description

小T准备在家里摆放几幅画,为此他买来了N幅画和N个画框。为了体现他的品味,小T希望能合理地搭配画与画框,使得其显得既不过于平庸也不太违和。对于第i幅画与第j个画框的配对,小T都给出了这个配对的平凡度Aij 与违和度Bij 。整个搭配方案的总体不和谐度为每对画与画框平凡度之和与每对画与画框违和度的乘积。具体来说,设搭配方案中第i幅画与第Pi个画框配对,则总体不和谐度为



小T希望知道通过搭配能得到的最小的总体不和谐度是多少。

Input

输入文件第 行是一个正整数T ,表示数据组数,接下来是T组数据。
对于每组数据,第 行是一个正整数N,表示有N对画和画框。
第2到第N+1行,每行有N个非负整数,第i+1 行第j个数表示Aij 。
第N+2到第2*N+1行,每行有N个非负整数,第i+N+1 行第j个数表示Bij 。

Output

包含T行,每行一个整数,表示最小的总体不和谐度

Sample Input

1

3

4 3 2

2 3 4

3 2 1

2 3 2

2 2 4

1 1 3

Sample Output

30

HINT

第1幅画搭配第3个画框,第2幅画搭配第1个画框,第3 幅画搭配第2个画框,则总体不和谐度为30

N<=70,T<=3,Aij<=200,Bij<=200

正解:最小乘积$KM$。

学习了一下最小乘积生成树,突然意识到原来这是一道傻逼题,然后就把这题做了。。

这题跟最小乘积生成树几乎一模一样,只是把$kruskal$换成了$KM$而已。

我们考虑把所有解的$x$(也就是$a$)之和与$y$(也就是$b$)之和投影到笛卡尔坐标系上,那么每个解就对应一个点$(x,y)$。

我们要使$xy$最小,很显然,使得$xy$最小的点一定在反比例函数$xy=k$上,其中$k$也是最小的。

然后感性地发现,最优解一定在所有解组成的点的上凸包上。

那么我们就有一个递归的做法,首先求出只有$x$或$y$的最优解$A$和$B$,也就是直接跑$KM$,那么实际最优解一定在这两个点与原点之间的那个区域上。

然后我们可以找到离$AB$在原点方向上最远的点$C$,递归做$A,C$与$B,C$即可。

现在有一个问题,就是如何找$C$。

因为$C$离$AB$最远,所以我们可以得到$S\Delta ABC$最大的结论。

$\vec{AB}=(B.y-A.y,B.x-A.x)$,$\vec{AC}=(C.y-A.y,C.x-A.x)$,而这两个向量叉积的一半即为$S\Delta ABC$。

因为叉积是有向的,所以我们实际上要使$\vec{AB}*\vec{AC}$最小,也就是使得$(B.x-A.x)*(C.y-A.y)-(B.y-A.y)*(C.x-A.x)$最小。

那么把它拆开,我们可以得到$(B.x-A.x)*C.y+(A.y-B.y)*C.x-A.y*(B.x-A.x)+A.x*(B.y-A.y)$。

那么只有前面那一半是不定的,我们可以发现,只要把$g[i][j]$改成$(B.x-A.x)*y[i][j]+(A.y-B.y)*x[i][j]$,再跑一遍$KM$,就能找到$C$了。

然后我们递归就行了,终止条件就是叉积$>=0$。由于要求最小权匹配,我们把边权取反就行了。

//It is made by wfj_2048~
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define inf (1<<30)
#define il inline
#define RG register
#define ll long long

using namespace std;

int visx[110],visy[110],lx[110],ly[110],lk[110],slack[110],cnt;
int g[110][110],a[110][110],b[110][110],n;

struct point{ int x,y; }ans,ansa,ansb;

il point operator - (const point &A,const point &B){
return (point){A.x-B.x,A.y-B.y};
}

il int cross(RG point A,RG point B){ return A.x*B.y-A.y*B.x; }

il int gi(){
RG int x=0,q=1; RG char ch=getchar();
while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
if (ch=='-') q=-1,ch=getchar();
while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar();
return q*x;
}

il int dfs(RG int x){
visx[x]=cnt;
for (RG int y=1;y<=n;++y){
if (visy[y]==cnt) continue;
if (lx[x]+ly[y]==g[x][y]){
visy[y]=cnt;
if (!lk[y] || dfs(lk[y])){ lk[y]=x; return 1; }
} else slack[y]=min(slack[y],lx[x]+ly[y]-g[x][y]);
}
return 0;
}

il point KM(){
for (RG int i=1;i<=n;++i) lk[i]=ly[i]=0,lx[i]=-inf;
for (RG int i=1;i<=n;++i)
for (RG int j=1;j<=n;++j) lx[i]=max(lx[i],g[i][j]);
for (RG int x=1;x<=n;++x){
for (RG int i=1;i<=n;++i) slack[i]=inf;
while (++cnt,!dfs(x)){
RG int d=inf;
for (RG int i=1;i<=n;++i)
if (visy[i]!=cnt) d=min(d,slack[i]);
for (RG int i=1;i<=n;++i){
if (visx[i]==cnt) lx[i]-=d;
if (visy[i]==cnt) ly[i]+=d; else slack[i]-=d;
}
}
}
RG point now=(point){0,0};
for (RG int i=1;i<=n;++i)
now.x+=a[lk[i]][i],now.y+=b[lk[i]][i];
RG int Ans=ans.x*ans.y,Now=now.x*now.y;
if (Ans>Now) ans=now; return now;
}

il void solve(RG point A,RG point B){
for (RG int i=1;i<=n;++i)
for (RG int j=1;j<=n;++j)
g[i][j]=-(b[i][j]*(B.x-A.x)+a[i][j]*(A.y-B.y));
RG point C=KM(); if (cross(B-A,C-A)>=0) return;
solve(A,C),solve(C,B); return;
}

il void work(){
n=gi(),ans.x=ans.y=40000;
for (RG int i=1;i<=n;++i)
for (RG int j=1;j<=n;++j) a[i][j]=gi();
for (RG int i=1;i<=n;++i)
for (RG int j=1;j<=n;++j) b[i][j]=gi();
for (RG int i=1;i<=n;++i)
for (RG int j=1;j<=n;++j) g[i][j]=-a[i][j];
ansa=KM();
for (RG int i=1;i<=n;++i)
for (RG int j=1;j<=n;++j) g[i][j]=-b[i][j];
ansb=KM();
solve(ansa,ansb),printf("%d\n",ans.x*ans.y); return;
}

int main(){
#ifndef ONLINE_JUDGE
freopen("frame.in","r",stdin);
freopen("frame.out","w",stdout);
#endif
RG int T=gi();
while (T--) work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: