二分图KM算法 POJ 2195

来源:互联网 发布:虹口js漂亮的油压店 编辑:程序博客网 时间:2024/05/21 17:59

暴力(n^3)

#include<cstdio>#include<cmath>#include<cstring>#include<algorithm>using namespace std;#define MAX 105#define INF 9999999struct House { int r, c; } house[MAX];struct Man { int r, c; } man[MAX];int H, M, n, m;int A[MAX], B[MAX];int visA[MAX], visB[MAX];int match[MAX], slack[MAX], map[MAX][MAX];bool find_path ( int i ){visA[i] = true;for ( int j = 0; j < H; j++ ){if ( !visB[j] && A[i] + B[j] == map[i][j] ){visB[j] = true;if (match[j] == -1 || find_path(match[j])){match[j] = i;return true;}}else if ( A[i] + B[j] > map[i][j] ) //j属于B,且不在交错路径中slack[j] = min(slack[j], A[i]+B[j]-map[i][j]);}return false;}void KM (){    int i, j, d;    memset(A,0,sizeof(A));    memset(B,0,sizeof(B));    memset(match,-1,sizeof(match));    for ( i = 0; i < M; i++ )        for ( j = 0; j < H; j++ )            A[i] = max (map[i][j], A[i]);    for ( i = 0; i < M; i++ )    {        for ( j = 0; j < H; j++ )            slack[j] = INF;        while ( 1 )        {            memset(visA,0,sizeof(visA));            memset(visB,0,sizeof(visB));            if ( find_path ( i ) ) break; //从i点出发找到交错路径则跳出循环            for ( d = INF, j = 0; j < H; j++ ) //取最小的slack[j]                if (!visB[j] && d > slack[j]) d = slack[j];            for ( j = 0; j < M; j++ ) //集合A中位于交错路径上的-d                if ( visA[j] ) A[j] -= d;            for ( j = 0; j < H; j++ ) //集合B中位于交错路径上的+d                if ( visB[j] ) B[j] += d;                else slack[j] -= d; //注意修改不在交错路径上的slack[j]        }    }}int main(){    char s[MAX];int i, j, res;while ( scanf("%d%d",&n,&m) ){    if ( !m && !n ) break;H = M = res = 0;for ( i = 0; i < n; i++ ){scanf("%s",s);for ( j = 0; j < m; j++ ){if ( s[j] == 'H' )                    house[H].r = i, house[H++].c = j;else if ( s[j] == 'm' )man[M].r = i, man[M++].c = j;}}for ( i = 0; i < M; i++ ) //求最小带权匹配可以将权值改为负数for ( j = 0; j < H; j++ )map[i][j] = -(abs(man[i].r-house[j].r) + abs(man[i].c-house[j].c));        KM();for ( j = 0; j < H; j++ )res -= map[match[j]][j];printf("%d\n",res);}return 0;}


KM算法

#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;const int maxn = 105;const int INF = 1<<29;int w[maxn][maxn];int n;int ly[maxn],lx[maxn];//顶标bool S[maxn],T[maxn]; //左/右第i个点是否已经标记int left[maxn];       //匹配为右边第i个点的编号int H, M;int R,C;bool match(int i){   S[i]=1;   for(int j=1;j<=n;j++)       if(lx[i]+ly[j]==w[i][j]&&!T[j]){          T[j]=1;          if(!left[j]||match(left[j])){            left[j]=i;            return true;          }       }   return false;}void update(){    int a=INF;    for(int i=1;i<=n;i++){        if(S[i])        for(int j=1;j<=n;j++){            if(!T[j])                a=min(a,lx[i]+ly[j]-w[i][j]);        }    }    for(int i=1;i<=n;i++){        if(S[i]) lx[i]-=a;        if(T[i]) ly[i]+=a;    }}void KM(){    for(int i=1;i<=n;i++){        left[i]=lx[i]=ly[i]=0;        for(int j=1; j<=n;j++){            lx[i]=max(lx[i],w[i][j]);        }    }    for(int i=1;i<=n;i++){        while(1){                //printf("1");            memset(T,0,sizeof(T));            memset(S,0,sizeof(S));            if(match(i)) break;            else update();        }    }}struct House { int r, c; } house[maxn];struct Man { int r, c; } man[maxn];int main(){    char s[maxn];int i, j;while ( scanf("%d %d",&R,&C) ){    if ( !R && !C ) break;H = M = 1;for ( i = 1; i <=R; i++ ){scanf("%s",s+1);for ( j = 1; j <=C; j++ ){if ( s[j] == 'H' )                    house[H].r = i, house[H++].c = j;else if ( s[j] == 'm' )man[M].r = i, man[M++].c = j;}}for ( i = 1; i <= M-1; i++ ) //求最小带权匹配可以将权值改为负数for ( j = 1; j <= H-1; j++ )w[i][j] = -(abs(man[i].r-house[j].r) + abs(man[i].c-house[j].c));        n=H-1;        KM();        int res=0;for ( j = 1; j <=H-1; j++ )res -= w[left[j]][j];printf("%d\n",res);}return 0;}


0 0
原创粉丝点击