http://poj.org/problem?id=2400

KM最大匹配

第一次KM的题目

详解就不说了 可以看这里:http://blog.163.com/huangbingliang@yeah/blog/static/94161399201011291044527/

我的代码和思路基本上市抄了别人的

不过还是要整理一下

1,KM 求最佳匹配

2,dfs求所有答案

还是看代码吧:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>

using namespace std;
const int MAX=0x7ffffff;
const int N=17;
int point[N][N];
int a[N],b[N];//二分图分两组 两组的顶标
int f[N];//指向
int sum;//最佳匹配答案
int I,n;
bool lv[N],rv[N];//是否在交叉树内
bool dfs(int x)//匈牙利算法
{
    lv[x]=true;
    for(int i=1;i<=n;++i)
    {
        if(!rv[i]&&a[x]+b[i]==point[x][i])
        {
            rv[i]=true;
            if(f[i]==-1||dfs(f[i]))
            {
                f[i]=x;
                return true;
            }
        }
    }
    return false;
}
int KM()
{
    memset(b,0,sizeof(b));
    for(int i=1;i<=n;++i)
    {
        a[i]=-MAX;
        for(int j=1;j<=n;++j)
        {
            a[i]=max(a[i],point[i][j]);//a组顶标找最大
        }
    }
    memset(f,-1,sizeof(f));
    for(int l=1;l<=n;++l)
    {
        while(1)
        {
            memset(lv,false,sizeof(lv));
            memset(rv,false,sizeof(rv));
            if(dfs(l))//如果第l个找成功 跳出循环找下一个,否则修改顶标继续找
            break;
            int d=MAX;
            for(int i=1;i<=n;++i)
            {
                if(lv[i])
                {
                    for(int j=1;j<=n;++j)
                    {
                        if(!rv[j])
                        {
                            d=min(d,a[i]+b[j]-point[i][j]);//确定修改顶标最小值
                        }
                    }
                }
            }
            for(int i=1;i<=n;++i)
            {
                if(lv[i])
                a[i]-=d;
                if(rv[i])
                b[i]+=d;//修改顶标
            }
        }

    }
    sum=0;
    for(int i=1;i<=n;++i)
    {
        if(f[i]!=-1)
        {
            sum+=(-point[f[i]][i]);//最近匹配答案
        }
    }
    return sum;
}
void  Outdfs(int x,int s)//答案输出
{
    if(x==n+1)
    {
        if(s==sum)
        {
            printf("Best Pairing %d\n",I);
            ++I;
            for(int i=1;i<=n;++i)
            {
                printf("Supervisor %d with Employee %d\n",i,f[i]);
            }
        }
       return;
    }
    for(int i=1;i<=n;++i)
    {
        if(!rv[i]&&a[x]+b[i]==point[x][i])
        {
            rv[i]=true;
            f[x]=i;
            Outdfs(x+1,s-point[x][i]);
            rv[i]=false;
        }
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    for(int w=1;w<=T;++w)
    {
        scanf("%d",&n);
        memset(point,0,sizeof(point));
        int k;
        for(int i=1;i<=n;++i)
        {
            for(int j=0;j<n;++j)
            {
                scanf("%d",&k);
                point[k][i]-=j;//原题求最小  转化为求最大
            }
        }
        for(int i=1;i<=n;++i)
        {
            for(int j=0;j<n;++j)
            {
                scanf("%d",&k);
                point[i][k]-=j;
            }
        }
        printf("Data Set %d, Best average difference: %.6f\n",w,1.0*KM()/2.0/n);
        memset(rv,false,sizeof(rv));
        I=1;
        Outdfs(1,0);
        printf("\n");
    }
    return 0;
}

 

posted on 2012-06-10 13:30  夜->  阅读(247)  评论(0编辑  收藏  举报