1. 程式人生 > >二分圖 km演算法模板

二分圖 km演算法模板

參考連結:https://blog.csdn.net/sixdaycoder/article/details/47720471

https://www.cnblogs.com/Lanly/p/6291214.html

https://www.cnblogs.com/Mychael/p/8994980.html

 

題目:hdu 2255

#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

#define INF 0x3f3f3f3f

const int maxn=305;

int match[maxn],lx[maxn],ly[maxn],slack[maxn];
int G[maxn][maxn];

bool visx[maxn],visy[maxn];

int n,nx,ny;

bool findpath(int x)
{
    int tempdelta;

    visx[x]=1;

    for(int y=0;y<ny;y++)
    {
        if(visy[y]) continue;
        tempdelta=lx[x]+ly[y]-G[x][y];

        if(tempdelta==0){
            visy[y]=1;
            if(match[y]==-1||findpath(match[y])){
                match[y]=x;
                return true;
            }
        }
        else if(slack[y]>tempdelta)
            slack[y]=tempdelta;
    }

    return false;
}

void KM()
{
    for(int x=0;x<nx;x++)
    {

        for(int j=0;j<ny;j++) slack[j]=INF;

        while(1)
        {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));

            if(findpath(x)) break;

            int delta=INF;

            for(int j=0;j<ny;j++)
                if(!visy[j]) delta=min(delta,slack[j]);

            for(int i=0;i<nx;i++)
            {
                if(visx[i]) lx[i]-=delta; ///已匹配點處理
                if(visy[i]) ly[i]+=delta;
                else slack[i]-=delta;
            }
        }
    }
}

void solve()
{
    memset(match,-1,sizeof(match));
    memset(ly,0,sizeof(match));

    for(int i=0;i<nx;i++)
    {
        lx[i]=-INF;
        for(int j=0;j<ny;j++)
            lx[i]=max(lx[i],G[i][j]);
    }

    KM();
}

int main()
{

    while(~scanf("%d",&n))
    {
        nx=ny=n;

        for(int i=0;i<nx;i++)
            for(int j=0;j<ny;j++)
            scanf("%d",&G[i][j]);

        solve();

        int ans=0;

        for(int i=0;i<ny;i++)
        {
            if(match[i]!=-1)
                ans+=G[match[i]][i];
        }
        printf("%d\n",ans);
    }
}

 

題目:hdu 1533

題意:給幅圖,有相同的人和相同的房子,讓你把每個人移動到房子裡,每移動一個單位,要出一塊錢,問你:將所有人移動到房子裡,最少移動步數是多少?

題解:直接建個二分圖。

///題目要的是最短路,所以我們把值取負,就相當於km演算法求了

#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

#define INF 0x3f3f3f3f

const int maxn=110;

char op[maxn][maxn];

int match[maxn],lx[maxn],ly[maxn],slack[maxn];
bool visx[maxn],visy[maxn];

struct node{
    int x,y;
    node(){}
    node(int _x,int _y){
        x=_x;y=_y;
    }
}house[maxn],people[maxn]; ///儲存房子,人的位置

int G[maxn][maxn],nx,ny;

bool findpath(int x)
{
    int tempdelta;

    visx[x]=1;

    for(int y=0;y<ny;y++)
    {
        if(visy[y]) continue;
        tempdelta=lx[x]+ly[y]-G[x][y];

        if(tempdelta==0)
        {
            visy[y]=1;

            if(match[y]==-1||findpath(match[y])){
                match[y]=x;
                return 1;
            }
        }
         else if(slack[y]>tempdelta) slack[y]=tempdelta;
    }

    return false;


}

void KM()
{
    for(int x=0;x<nx;x++)
    {
//        printf("asdf");
        for(int j=0;j<ny;j++) slack[j]=INF;

        while(1)
        {
//            printf("tiao");
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));

            if(findpath(x)) break;

            int delta=INF;

            for(int j=0;j<ny;j++)
                if(!visy[j]) delta=min(delta,slack[j]);

            for(int i=0;i<nx;i++)
                if(visx[i]) lx[i]-=delta;

            for(int j=0;j<ny;j++)
            {
                if(visy[j]) ly[j]+=delta;
                else slack[j]-=delta;
            }
        }
    }
}

void solve() ///模板了
{
    memset(match,-1,sizeof(match));
    memset(ly,0,sizeof(ly));

    for(int i=0;i<nx;i++)
    {
        lx[i]=-INF;

        for(int j=0;j<ny;j++)
            lx[i]=max(lx[i],G[i][j]);
    }

    KM();
}

int main()
{

    int n,m;

    while(scanf("%d%d",&n,&m))
    {
        if(m==0&&n==0) break;

        for(int i=0;i<n;i++)
            scanf("%s",op[i]);

        nx=ny=0;

        for(int i=0;i<n;i++)
        for(int j=0;j<m;j++){
            if(op[i][j]=='m') people[nx++]=node(i,j); ///儲存點
            else if(op[i][j]=='H') house[ny++]=node(i,j);
        }

        memset(G,0,sizeof(G));

        for(int i=0;i<nx;i++)
        {
            for(int j=0;j<ny;j++) ///建圖
            {
                
                G[i][j]=-(abs(house[j].x-people[i].x)+abs(house[j].y-people[i].y));
            }
        }

        solve();

        int ans=0;

        for(int i=0;i<ny;i++)
        {
            if(match[i]!=-1) ans+=G[match[i]][i];
        }
        printf("%d\n",-ans);
    }
    return 0;
}