poj 2400 Supervisor, Supervisee 二分匹配 最小权完美匹配 KM算法

           题意:有n个应聘者应聘n个部门,每个部门的经理对每个人的喜欢程度不一样(记1~N,最喜欢的为1),每个应聘者对每个经理的喜欢程度也不一样(同上)。给出每个经            理对应聘者的喜欢程度和应聘者对经理的喜欢程度,求平均最小偏差(貌似题目给出的两个矩阵是反的,有点不清晰啊)

           思路:此题求最小权的完美匹配,cv[i][j]记录i候选人对j经理的喜爱程度,sv[i][j]记录i经理对j候选人的喜爱程度。w[i][j]表示i匹配j的偏差:-(sv[j][i]+cv[i][j]-2)(注:这里用KM算法求最小权,则w=-w或者可以用large-w来表示)。

代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#define maxn 20
#define INF 0x3f3f3f3f
#define clr(a,b) memset(a,b,sizeof(a))
#define max(a,b) a>b?a:b
#define min(a,b) a<b?a:b

using namespace std;
bool ms;
int sx[maxn],sy[maxn],w[maxn][maxn],mat[maxn],vis[maxn];
int g[maxn][maxn],lx[maxn],ly[maxn],sv[maxn][maxn],cv[maxn][maxn];
int n,ca;
int cas=1,c,ans,q;
void init()
{
    for(int i=1;i<=n;i++)
       for(int j=1;j<=n;j++)
           w[i][j]=-(sv[j][i]+cv[i][j]-2);
}
bool find(int x)
{
    sx[x]=1;
    for(int i=1;i<=n;i++)
    {
        if(!sy[i]&&lx[x]+ly[i]==w[x][i])
        {
            sy[i]=1;
            if(mat[i]==-1||find(mat[i]))
             {
                 mat[i]=x;
                 return 1;
             }
        }
    }
    return 0;
}
int KM()
{
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        ly[i]=0;
        lx[i]=-INF;
        for(int j=1;j<=n;j++)
          lx[i]=max(lx[i],w[i][j]);
    }
    clr(mat,-1);
    for(int i=1;i<=n;i++)
     while(1)
     {
         clr(sx,0);
         clr(sy,0);
         if(find(i)) break;
         int d=INF;
         for(int i=1;i<=n;i++)
         {
             if(sx[i])
             {
                 for(int j=1;j<=n;j++)
                 {
                     if(!sy[j])
                      d=min(lx[i]+ly[j]-w[i][j],d);
                 }
             }
         }
         for(int i=1;i<=n;i++)
         {
             if(sx[i])
               lx[i]-=d;
             if(sy[i])
               ly[i]+=d;
         }
     }
     for(int i=1;i<=n;i++)
        sum+=w[mat[i]][i];
    return -sum;
}
void dfs(int id,int s)
{
    if(s>ans) return ;
    if(id==n+1)
    {
        printf("Best Pairing %d\n",q++);
        for(int i=1;i<=n;i++)
         printf("Supervisor %d with Employee %d\n",i,mat[i]);
    }
    else
    {
        for(int j=1;j<=n;j++)
        {
            if(!vis[j])
            {
               vis[j]=1;
               mat[id]=j;
               dfs(id+1,s-w[mat[id]][id]);
               vis[j]=0;
            }
        }
    }
}
int main()
{
    scanf("%d",&ca);
    while(ca--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
          for(int j=1;j<=n;j++)
             {
                 scanf("%d",&c);
                 cv[i][c]=j;
             }
        for(int i=1;i<=n;i++)
          for(int j=1;j<=n;j++)
             {
                 scanf("%d",&c);
                 sv[i][c]=j;
             }
        init();
        q=1;
        ans=KM();
        printf("Data Set %d, Best average difference: %lf\n",cas++,ans/(2.0*n));
        clr(vis,0);
        dfs(1,0);
        puts("");
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值