POJ 2195 二分圖最小權匹配KM演算法
阿新 • • 發佈:2019-01-04
本來是打算昨天晚上寫的, 昨天網速渣的連CSDN都進不去,沒辦法 只能現在來寫了
先寫寫對KM演算法的理解,KM演算法是對每個點設定一個頂標,只有當邊長等於兩邊點的頂標之和的時候才進行增廣,這樣就能保證得到的一定是最大權匹配。
如果找不到匹配的時候就對交替路中X集合的頂標減少一個d Y集合的頂標增加一個d。
這樣兩個點都在交替路中的時候x[i]+y[i]的和不邊
X在 Y不在的時候x[i]+y[i]減少,可能就會為圖增加一對匹配。
X不在Y在的時候x[i]+y[i]增加, 原來不在現在依然不在其中。
題意:有n個人n個房子,每人進一個房子,求最少的總距離。
思路:對每條邊取相反數,然後得到的結果再取相反數,就能得到最小權匹配。
#include<stdio.h> #include<string.h> #include<vector> #include<algorithm> #include<math.h> #include<iostream> #define INF 10000000 using namespace std; char map[105][105]; struct point{ int x,y; }X[105],Y[105]; int W[105][105]; int macy[105]; bool check[105]; bool checkx[105]; int zx[105]; int zy[105]; int Y1[105]; int n; bool dfs(int u) { checkx[u]=1; for(int i=0;i<n;i++) { if(zx[u]+zy[i]==W[u][i]&&!check[i]) { check[i]=1; if(macy[i]==-1||dfs(macy[i])) { macy[i]=u; return 1; } } if(!check[i]) { Y1[i]=min(Y1[i],zx[u]+zy[i]-W[u][i]); } } return 0; } void gx() { int a=INF; for(int i=0;i<n;i++) if(!check[i]) a=min(a,Y1[i]); for(int i=0;i<n;i++) { if(check[i]) zy[i]+=a; if(checkx[i]) zx[i]-=a; } } void xyl() { memset(macy,-1,sizeof(macy)); for(int i=0;i<n;i++) { memset(check,0,sizeof(check)); memset(checkx,0,sizeof(checkx)); if(!dfs(i)) { i--; gx(); } } } int main() { int N,M; while(scanf("%d%d",&N,&M)!=EOF) { if(N==0&&M==0) return 0; for(int i=0;i<N;i++) scanf("%s",map[i]); int r1=0,r2=0; for(int i=0;i<N;i++) for(int j=0;j<M;j++) { if(map[i][j]=='H') { X[r1].x=i; X[r1].y=j; r1++; } else if(map[i][j]=='m') { Y[r2].x=i; Y[r2].y=j; r2++; } } memset(zy,0,sizeof(zy)); for(int i=0;i<r1;i++) { zx[i]=-INF; for(int j=0;j<r2;j++) { W[i][j]=abs(X[i].x-Y[j].x)+abs(X[i].y-Y[j].y); W[i][j]*=-1; zx[i]=max(zx[i],W[i][j]); Y1[j]=INF; } } n=r1; //return 0; xyl(); int p=0; for(int i=0;i<n;i++) { int u=macy[i]; p+=W[u][i]; } printf("%d\n",-p); } }