二分图最大带权匹配:KM算法

二分图最大带权匹配。

输入点的个数和各边权值,输出最大匹配的权值和。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4  
 5 using namespace std;
 6  
 7 const int N=310;
 8 const int INF=0x3f3f3f3f;
 9  
10 int n,nx,ny;
11 int linker[N],lx[N],ly[N],slack[N];  //lx,ly为顶标,nx,ny分别为x点集y点集的个数
12 int visx[N],visy[N],w[N][N];
13  
14 int DFS(int x){
15     visx[x]=1;
16     for(int y=1;y<=ny;y++){
17         if(visy[y])
18             continue;
19         int tmp=lx[x]+ly[y]-w[x][y];
20         if(tmp==0){
21             visy[y]=1;
22             if(linker[y]==-1 || DFS(linker[y])){
23                 linker[y]=x;
24                 return 1;
25             }
26         }else if(slack[y]>tmp){ //不在相等子图中slack 取最小的
27             slack[y]=tmp;
28         }
29     }
30     return 0;
31 }
32  
33 int KM(){
34     int i,j;
35     memset(linker,-1,sizeof(linker));
36     memset(ly,0,sizeof(ly));
37     for(i=1;i<=nx;i++)      //lx初始化为与它关联边中最大的
38         for(j=1,lx[i]=-INF;j<=ny;j++)
39             if(w[i][j]>lx[i])
40                 lx[i]=w[i][j];
41     for(int x=1;x<=nx;x++){
42         for(i=1;i<=ny;i++)
43             slack[i]=INF;
44         while(1){
45             memset(visx,0,sizeof(visx));
46             memset(visy,0,sizeof(visy));
47             if(DFS(x))  //若成功(找到了增广轨),则该点增广完成,进入下一个点的增广
48                 break;  //若失败(没有找到增广轨),则需要改变一些点的标号,使得图中可行边的数量增加。
49                         //方法为:将所有在增广轨中(就是在增广过程中遍历到)的X方点的标号全部减去一个常数d,
50                         //所有在增广轨中的Y方点的标号全部加上一个常数d
51             int d=INF;
52             for(i=1;i<=ny;i++)
53                 if(!visy[i] && d>slack[i])
54                     d=slack[i];
55             for(i=1;i<=nx;i++)
56                 if(visx[i])
57                     lx[i]-=d;
58             for(i=1;i<=ny;i++)  //修改顶标后,要把所有不在交错树中的Y顶点的slack值都减去d
59                 if(visy[i])
60                     ly[i]+=d;
61                 else
62                     slack[i]-=d;
63         }
64     }
65     int res=0;
66     for(i=1;i<=ny;i++)
67         if(linker[i]!=-1)
68             res+=w[linker[i]][i];
69     return res;
70 }
71  
72 int main(){
73  
74     //freopen("input.txt","r",stdin);
75  
76     while(~scanf("%d",&n)){
77         nx=ny=n;
78         for(int i=1;i<=n;i++)
79             for(int j=1;j<=n;j++)
80                 scanf("%d",&w[i][j]);
81         int ans=KM();
82         printf("%d\n",ans);
83     }
84     return 0;
85 }

 

posted @ 2019-11-27 20:54  Lovaer  阅读(433)  评论(0编辑  收藏  举报