HDU2255(最全权完美匹配)

最大权完美匹配:二分图最大匹配是寻找最大匹配数,用匈牙利算法。当连 接的边带有权值时,要寻找匹配后权值和最大的方案,且保证 A 集合中的点均有 B 中的点能匹配。此时问题就转化为二分图最大权完美匹配。

KM 算法核心为: 为每一点添加顶标, 在顶标的限制下用匈牙利算法处理出最大匹配数, 若最大匹配数 =n, 则达到最优解, 输出。否则修改
顶标, 再用匈牙利算法处理, 如此重复。

设二分图的两部分点集分别为 X={X1,X2,…,Xn}X={X1,X2,…,Xn} 和 Y={Y1,Y2,…,Ym}Y={Y1,Y2,…,Ym}, ⟨Xi,Yj⟩⟨Xi,Yj⟩ 的边权为 wij
给两部分点集分别赋点权 {Ai},{Bi}{Ai},{Bi}, 使得 Ai+Bj⩾wij,取等的边的生成子图叫做相等子图。那么相等子图的完美匹配就是最大权匹配。我们需要适当选取权值,使相等子图有完美匹配。
算法步骤:
1.若成功(找到了增广轨),则该点增广完成,进入下一个点的增广
2.若失败(没有找到增广轨),则需要改变一些点的标号,使得图中可行边的数量增加。
操作为:将所有在增广轨中(就是在增广过程中遍历到)的X方点的标号全 部减去一个常数d,所有在增广轨中的Y方点的标号全部加上一个常数d
我也刚开始学习这个,也是跟着网上大佬的代码跟着敲的!

模版一:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include<cmath>
#include<vector>
#include<stack>
using namespace std;
const int maxx=505;
const int inf = 0x3f3f3f3f;
int n;
int linker[maxx],lx[maxx],ly[maxx],slack[maxx];  //lx,ly为顶标
int visx[maxx],visy[maxx],w[maxx][maxx];
int DFS(int x){
    visx[x]=1;
    for(int y=1;y<=n;y++){
        if(visy[y])continue;    
        int tmp=lx[x]+ly[y]-w[x][y];   
        if(tmp==0){   
            visy[y]=1;
            if(linker[y]==-1 || DFS(linker[y])){   
                linker[y]=x;
                return 1;
            }
        }else {
        	slack[y]=min(slack[y],tmp);
		}
    }
    return 0;
}

int KM(){
    int i,j;
    memset(linker,-1,sizeof(linker));
    memset(ly,0,sizeof(ly));  
    for(i=1;i<=n;i++)      
        for(j=1,lx[i]=-inf;j<=n;j++){
        	if(w[i][j]>lx[i]){
        		lx[i]=w[i][j];
			} 
		}
    for(int x=1;x<=n;x++){
        for(i=1;i<=n;i++){
        	slack[i]=inf;
		}
        while(true){
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            if(DFS(x))break;   
            int d=inf;
            for(i=1;i<=n;i++){
            	if(!visy[i])d=min(slack[i],d);   
			}
            for(i=1;i<=n;i++){
            	if(visx[i])lx[i]-=d;
			}
            for(i=1;i<=n;i++){
            	if(visy[i]){
            		ly[i]+=d;
				}else {
			    	slack[i]-=d;    
		    	}
			}            
        }
    }
    int res=0;
    for(i=1;i<=n;i++){
    	if(linker[i]!=-1){
    		res+=w[linker[i]][i];
		}
	}
    return res;
}
int main(){
    while(~scanf("%d",&n)){
        for(int i=1;i<=n;i++){
        	for(int j=1;j<=n;j++){
        		scanf("%d",&w[i][j]);
			}
		}
        int ans=KM();
        printf("%d\n",ans);
    }
    return 0;
}
模版二:超时
#include<iostream>
#include<algorithm>
#include<stack>
#include<cmath>
#include<vector>
#include<cstring>
using namespace std;
const int maxx=500;
const int inf=0x3f3f3f3f;
int n,m,w[maxx][maxx];
int mb[maxx],vb[maxx],ka[maxx],kb[maxx],p[maxx],c[maxx];
int qf,qb,q[maxx];
void Bfs(int u){
    int a,v=0,vl=0,d;
    for(int i=1;i<=n;i++) {
    	p[i]=0,c[i]=inf;
	}
    mb[v]=u;
    do {
        a=mb[v],d=inf,vb[v]=1;
        for(int b=1;b<=n;b++)if(!vb[b]){
            if(c[b]>ka[a]+kb[b]-w[a][b]){
            	c[b]=ka[a]+kb[b]-w[a][b];
				p[b]=v;
			}
            if(c[b]<d) {
            	d=c[b];
				vl=b;
			}
        }
        for(int b=0;b<=n;b++){
        	if(vb[b]) {
        		ka[mb[b]]-=d;
				kb[b]+=d;
			}else {
				c[b]-=d;
			}
		} 
        v=vl;
    } while(mb[v]);
    while(v) {
    	mb[v]=mb[p[v]];
		v=p[v];
	}
}
void init(){
	memset(ka,0,sizeof(ka));
	memset(kb,0,sizeof(kb));
	memset(mb,0,sizeof(mb));
	memset(p,0,sizeof(p));
	memset(vb,0,sizeof(vb));
	memset(c,0,sizeof(c));
	memset(q,0,sizeof(q));
}
int KM(){
    for(int a=1;a<=n;a++){
    	for(int b=1;b<=n;b++) {
    		vb[b]=0;
		}
		Bfs(a);
	}
	int res=0;
	for(int b=1;b<=n;b++) {
		res+=w[mb[b]][b];
	}
	return res;
}
int main(){
	while(scanf("%d",&n)){
		init();
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				scanf("%d",&w[i][j]);
			}
		}
		int ans=KM();
		cout<<ans<<endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值