BZOJ3571[HNOI2014]画框

题目链接:[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);
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值