zoukankan      html  css  js  c++  java
  • BZOJ3571: [Hnoi2014]画框

    BZOJ3571: [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


    题解Here!

    这题目难道掉牙了。。。
    我们会发现这个很像最小乘积生成树。
    于是我们把一种匹配的$sum A_i$和$sum B_i$分别看作横纵坐标,然后找$x imes y$最小的点。
    可以先找出$x$最小的点$A$和$y$最小的点$B$,然后找$AB$左下方离$AB$最远的点$C$。
    即$overrightarrow{AB} imesoverrightarrow{AC}$最小。
    然后把$overrightarrow{AB} imesoverrightarrow{AC}$拆开:
    $$left.egin{array}{}overrightarrow{AB} imesoverrightarrow{AC}&=&(x_B-x_A) imes(y_C-y_A)-(y_B-y_A) imes(x_C-x_A) \&=&(x_B-x_A) imes y_C+(y_A-y_B) imes x_C+y_B imes x_A-x_B imes y_Aend{array} ight.$$    

    在程序中我的$A,B,C$用$one,two,three$代替了,注意一下。。。

    那么把权值改成$map[i][j]=(y_A-y_B) imes A[i][j]+(x_B-x_A) imes B[i][j]$,再用带权二分图匹配就可以找出$C$。
    带权二分图匹配这个丢给了$KM$,当然费用流也是可行的,不过我比较懒就不写了。。。
    找出$C$后用叉积判断$C$是不是在$AB$的下方,如果是,就递归解决线段$AC$和$CB$。
    然后就做完了。。。
    没想到头一次写$KM$还是在这道题,我连板子题还没写呢。。。
    没学会走,先学会飞了。。。
    附代码:
    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #define MAXN 80
    #define MAX 999999999
    using namespace std;
    int n,ans;
    int A[MAXN][MAXN],B[MAXN][MAXN];
    struct Vector{
    	int x,y;
    	friend Vector operator +(const Vector p,const Vector q){return (Vector){p.x+q.x,p.y+q.x};}
    	friend Vector operator -(const Vector p,const Vector q){return (Vector){p.x-q.x,p.y-q.y};}
    	friend int operator *(const Vector p,const Vector q){return p.x*q.y-p.y*q.x;}
    };
    inline int read(){
    	int date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    namespace G{
    	int f[MAXN],valx[MAXN],valy[MAXN],val[MAXN],map[MAXN][MAXN];
    	bool visx[MAXN],visy[MAXN];
    	inline void buildgraph(int x,int y){
    		for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)map[i][j]=-(x*A[i][j]+y*B[i][j]);
    	}
    	bool find(int x){
    		visx[x]=true;
    		for(int i=1;i<=n;i++){
    			if(visy[i])continue;
    			int t=valx[x]+valy[i]-map[x][i];
    			if(!t){
    				visy[i]=true;
    				if(f[i]==-1||find(f[i])){
    					f[i]=x;
    					return true;
    				}
    			}
    			else val[i]=min(val[i],t);
    		}
    		return false;
    	}
    	Vector KM(){
    		Vector s=(Vector){0,0};
    		memset(f,-1,sizeof(f));
    		memset(valx,0,sizeof(valx));
    		memset(valy,0,sizeof(valy));
    		for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)valx[i]=max(valx[i],map[i][j]);
    		for(int i=1;i<=n;i++){
    			memset(val,63,sizeof(val));
    			while(1){
    				memset(visx,false,sizeof(visx));
    				memset(visy,false,sizeof(visy));
    				if(find(i))break;
    				int w=MAX;
    				for(int i=1;i<=n;i++)if(!visy[i])w=min(w,val[i]);
    				for(int i=1;i<=n;i++)if(visx[i])valx[i]-=w;
    				for(int i=1;i<=n;i++){
    					if(visy[i])valy[i]+=w;
    					else val[i]-=w;
    				}
    			}
    		}
    		for(int i=1;i<=n;i++){
    			s.x+=A[f[i]][i];
    			s.y+=B[f[i]][i];
    		}
    		return s;
    	}
    }
    void solve(Vector one,Vector two){
    	G::buildgraph(one.y-two.y,two.x-one.x);
    	Vector three=G::KM();
    	ans=min(ans,three.x*three.y);
    	if(((two-one)*(three-one))>=0)return;
    	solve(one,three);solve(three,two);
    }
    void work(){
    	Vector one,two;
    	G::buildgraph(1,0);
    	one=G::KM();
    	G::buildgraph(0,1);
    	two=G::KM();
    	ans=min(one.x*one.y,two.x*two.y);
    	solve(one,two);
    	printf("%d
    ",ans);
    }
    void init(){
    	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();
    }
    int main(){
    	int t=read();
    	while(t--){
    		init();
    		work();
    	}
        return 0;
    }
    
  • 相关阅读:
    poj 1684 Lazy Math Instructor(字符串)
    STL内存配置器
    迭代器(iterators)
    类型萃取(type traits)
    hdu 2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活(多重背包+dp)
    hdoj 1114 Piggy-Bank(完全背包+dp)
    hdoj 2546 饭卡(0-1背包)
    hdoj 2620 Bone Collector(0-1背包)
    U3d开发个人总结
    Android软键盘的用法总结
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9693025.html
Copyright © 2011-2022 走看看