bzoj 3571: [Hnoi2014]画框

Description

小T准备在家里摆放几幅画,为此他买来了N幅画和N个画框。为了体现他的品味,小T希望能合理地搭配画与画框,使得其显得既不过于平庸也不太违和。对于第 幅画与第 个画框的配对,小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

Source

如果知道最小乘积生成树的话,那这题就是真的裸得不能再裸了;

具体操作是这样的,把一个最大匹配的∑ai看成横坐标,∑bi看成纵坐标,那么就是就对应着平面上的点(x,y),答案就是x*y;

我们发现答案一定在下凸壳上,然后我们考虑如何求解,与二维乘积最小生成树是一样的;

这里发一个最小乘积生成树的链接,说得特别好,主要是有图:http://www.cnblogs.com/autsky-jadek/p/3959446.html

具体思想就是确定一条直线,然后在左下方找一个距离最远的点,找这个点就是找三角形面积最大的点,这个就是把叉积的式子展开,然后用费用流找到那个点,

然后往下递归,直到左下角没有点,这个用叉积面积<=0判;

//MADE BY QT666
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=20050;
const int Inf=2147483647;
struct Point{
    int x,y;
};
int a[100][100],b[100][100],S,T,n,ans,tmp,cnt;
struct data{
    int head[N],to[N],nxt[N],s[N],cc[N],in[N],fa[N],vx[N],vy[N],q[N*10],dis[N],cost;
    void Addedge(int x,int y,int z,int u,int X,int Y) {
	to[++cnt]=y,s[cnt]=z,cc[cnt]=u,nxt[cnt]=head[x],head[x]=cnt,vx[cnt]=X,vy[cnt]=Y;
    }
    void lnk(int x,int y,int z,int u,int X,int Y) {
	Addedge(x,y,z,u,X,Y),Addedge(y,x,0,-u,-X,-Y);
    }
    bool spfa(Point &a) {
	for(int i=S; i<=T; i++) dis[i]=Inf,in[i]=0;
        int t=0,sum=1;
	q[0]=S,in[S]=1,dis[S]=0;
	while(t<sum) {
	    int x=q[t++];in[x]=0;
	    for(int i=head[x];i;i=nxt[i]) {
		int y=to[i];
		if(s[i]&&dis[y]>dis[x]+cc[i]) {
		    dis[y]=dis[x]+cc[i];fa[y]=i;
		    if(!in[y]) in[y]=1,q[sum++]=y;
		}
	    }
	}
	if(dis[T]==Inf) return 0;
	for(int i=fa[T];i;i=fa[to[i^1]]){
	    a.x+=vx[i];a.y+=vy[i];
	    s[i]--,s[i^1]++;
	}
	cost+=dis[T];
	return 1;
    }
    Point Mincost() {Point c=(Point){0,0};while(spfa(c));return c;;}
    void build(int X,int Y){
	memset(head,0,sizeof(head));cnt=1;
	for(int i=1;i<=n;i++){
	    for(int j=1;j<=n;j++){
		lnk(i,j+n,1,X*a[i][j]+Y*b[i][j],a[i][j],b[i][j]);
	    }
	    lnk(S,i,1,0,0,0);lnk(i+n,T,1,0,0,0);
	}
    }
}Gragh;
int Cross(Point a,Point b,Point c){
    return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
void solve(Point a,Point b){
    Gragh.build(a.y-b.y,b.x-a.x);Point C=Gragh.Mincost();
    ans=min(ans,C.x*C.y);
    if(Cross(b,a,C)<=0) return;
    solve(a,C);solve(C,b);
}
int main(){
    freopen("frame.in","r",stdin);
    freopen("frame.out","w",stdout);
    int t;scanf("%d",&t);
    while(t--){
	scanf("%d",&n);S=0,T=2*n+1;
	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]);
	}
	Gragh.build(1,0);
	Point A=Gragh.Mincost();
	Gragh.build(0,1);
	Point B=Gragh.Mincost();
	ans=Inf;ans=min(ans,min(A.x*A.y,B.x*B.y));
	solve(A,B);printf("%d\n",ans);
    }
    return 0;
}
 
posted @ 2017-10-11 14:35  qt666  阅读(240)  评论(0编辑  收藏  举报