匈牙利算法&KM算法
匈牙利算法(hungary)
详解请看 http://philoscience.iteye.com/blog/1754498
匈牙利算法是用来计算最大匹配,用了增广路思想
增广路径有如下特性:
1. 有奇数条边
2. 起点在二分图的X边,终点在二分图的Y边
3. 路径上的点一定是一个在X边,一个在Y边,交错出现。
4. 整条路径上没有重复的点
5. 起点和终点都是目前还没有配对的点,其他的点都已经出现在匹配子图中
6. 路径上的所有第奇数条边都是目前还没有进入目前的匹配子图的边,而所有第偶数条边都已经进入目前的匹配子图。奇数边比偶数边多一条边
7. 于是当我们把所有第奇数条边都加到匹配子图并把条偶数条边都删除,匹配数增加了1.
就是找是否存在增广路,增广路的找寻办法:
在X轴上找一点xi在Y轴上从0开始找寻,有两种情况1,本身yi未匹配那就是了 2.如果yi已经匹配那么dfs找与它匹配的xi是否能再匹配,如果能的话就是了
dfs实现
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 10; int visx[maxn],visy[maxn]; int match[maxn],map[maxn][maxn]; int nx,ny; void res; int find(int u) { visx[u] = 1; for(int v = 0; v < ny; v++){ if(!visy[v] && map[u][v]){ visy[v] = 1; if(match[v] == -1 || find(match[v])){ match[v] = u; return true; } } } return false; } void hungary() { res = 0; memset(match,-1,sizeof(match)); for(int i = 0 ; i < nx; i++){ memset(visx,0,sizeof(visx)); mesmet(visy,0,sizeof(visy)); if( find(i)) res++: } printf("%d\n",res); }
KM算法:
匈牙利算法是对于边权为1的情况,KM算法是针对每条边有权值的情况,用了贪心的思想
贪心思想:体现在加边上面,加边之前的处理与匈牙利算法一样用dfs找是否存在增广路,如果不存在增广路则加边,加的边的权值要最大,那么相减就要最小(原来是最大权值,现在变成次大权值),加的边是visy中没有匹配过的点与当前u形成的边。
Lx[i] + Ly[j] 变小(Lx[i]变小,但是Ly[j]没有变化),相当于要求的边权值变小了,可以向里面填边了,
因为要考虑如果出现相同子图,如果Ly[j]不加个a的话这条边就出去了(每次重新选择u点时都会把visx和visy初始化)
答案就是所有匹配的match[i]
O(n^4)
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 10; int visx[maxn],visy[maxn]; int Lx[maxn],Ly[maxn]; int map[maxn][maxn],match[maxn]; int n; int res = 0; int find(int u) { visx[u] = 1; for(int v = 0 ; v < n; v++){ if(!visy[v] && Lx[u] + Ly[v] == map[u][v]){ visy[v] = 1; if(ma tch[v] == -1 || find(match[v])){ match[v] = u; return 1; } } } return 0 ; } void update() { int a = 1 << 30; for(int i = 0; i < n; i++) if(visx[i]) for(int j = 0; j < n ;j++) if(!visy[j]) a = min(a,Lx[i]+Ly[j] - map[i][j]); for(int i = 0; i < n ; i++){ if(visx[i]) Lx[i] -= a;//允许加边 if(visy[i]) Ly[i] += a;//防止边出去 } } void KM() { memset(match,-1,sizeof(match)); for(int i = 0 ; i <n ; i++){ Lx[i] = Ly[i] = 0; for(int j = 0 ; j < n ; j++) Lx[i] = max(Lx[i],map[i][j]); } for(int i = 0 ; i < n ; i++){ for(; ; ){ memset(visx,0,sizeof(visx)); memset(visy,0,sizeof(visy)); if(find(i)) break; else update(); } } } int main() { scanf("%d",&n); int res = 0; KM(); for(int i = 0 ; i < n ; i++){ if(match[i]!=-1){ res += map[match[i]][i]; } } printf("%d\n",res); return 0; }