zoukankan      html  css  js  c++  java
  • poj 2400 Supervisor,Supervisee 夜

    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;
    }
    
    
  • 相关阅读:
    暴力枚举 --- 多方法求解
    图论 --- 骑士周游问题,DFS
    数论 --- 同余定理
    数论 --- 筛法求素数进一步优化
    求大素数
    codeforces --- Round #244 (Div. 2) B. Prison Transfer
    codeforces --- Round #244 (Div. 2) A. Police Recruits
    线段树 --- (区间维护+逆推)
    线段数 --- (单点更新、求逆序对)
    线段树 --- (单点更新、区间求和、模板题)
  • 原文地址:https://www.cnblogs.com/liulangye/p/2543972.html
Copyright © 2011-2022 走看看