pku 2400 Supervisor, Supervisee KM求最小权匹配+DFS回溯解集

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

题意:

有n个管理员需要雇佣n个工作人员。 每个管理员对每个工作人员的评价不同,评价值(score)从0-n-1,0代表评价最高,n-1代表评价最低,(这样处理用KMq求解时才能出现0)同样,每个工作人员对每个管理员也有不同 的评价,评价值也是从0-n-1,0代表评价值最高,n-1代表最低。问n个管理员怎样选择n个工作人员可以使的每个人的平均评价值最小。即总的评价值 /(2*n)最小。如果存在多种最佳方案,则按照字典序输出每一种情况。
思路:

我们把N个管理员与N个员工分成两个点集,X,Y.管理员X[i]与员工Y[j]总的评价值为X[i]对Y[j]的评价值+Y[j]对X[i]的评价值。然后建图求最小权匹配即可。KM求完之后得到完备匹配,由于所有可能的匹配肯定在所得到的完备匹配的相等子图上,在其上dfs所有可能结果即可。

#include <cstdio>
#include <cstring>
#include <iostream>
#define maxn 17
using namespace std;

const int inf = 99999999;
int w[maxn][maxn],link[maxn],match[maxn];
int lx[maxn],ly[maxn];
bool vtx[maxn],vty[maxn],vt[maxn];
int slack[maxn];
int n,cnt;

bool dfs(int i)
{
    int j;
    vtx[i] = true;
    for (j = 1; j <= n; ++j)
    {
        if (vty[j]) continue;
        int tmp = lx[i] + ly[j] - w[i][j];
        if (tmp == 0)
        {
            vty[j] = true;
            if (link[j] == -1 || dfs(link[j]))
            {
                link[j] = i;
                return true;
            }
        }
        else
        slack[j] = min(tmp,slack[j]);
    }
    return false;
}
int KM()
{
    int i,j;
    for (i = 1; i <= n; ++i)
    {
        for (j = 1,lx[i] = -inf; j <= n; ++j)
        {
            lx[i] = max(lx[i],w[i][j]);
        }
    }
    for (i = 1; i <= n; ++i)
    {
        ly[i] = 0; link[i] = match[i] = -1;
        vt[i] = false;
    }
    for (i = 1; i <= n; ++i)
    {
        for (j = 1; j <= n; ++j) slack[j] = inf;
        while (1)
        {
            for (j = 1;j <= n; ++j) vtx[j] = vty[j] = false;
            if (dfs(i)) break;
            int d = inf;
            for (j = 1; j <= n; ++j)
            {
                if (!vty[j] && d > slack[j])
                d = slack[j];
            }
            for (j = 1; j <= n; ++j)
            if (vtx[j]) lx[j] -= d;
            for (j = 1; j <= n; ++j)
            if (vty[j]) ly[j] += d;
            else slack[j] -= d;
        }
    }

    int sum = 0;
    for (i = 1; i <= n; ++i)
    if (link[i] > -1) sum -= w[link[i]][i];

    return sum;
}

void DfsRes(int i)
{
     if (i > n)
     {
         printf("Best Pairing %d\n",cnt++);
         for (i = 1; i <= n; ++i)
         {
             printf("Supervisor %d with Employee %d\n",i,match[i]);
         }
     }
     for (int j = 1; j <= n; ++j)
     {
         if (lx[i] + ly[j] == w[i][j] && !vt[j])
         {
             vt[j] = true;
             match[i] = j;
             DfsRes(i + 1);
             vt[j] = false;
         }
     }
}
int main()
{
    int i,j,t,k;
    int cas = 1;
    scanf("%d",&t);
    while (t--)
    {
        scanf("%d",&n);
        memset(w,0,sizeof(w));
        /*
          题目给定的关系矩阵给反了所以建图有点纠结了;
          意思不变。
        */
        for (i = 1; i <= n; ++i)
        {
            for (j = 0; j < n; ++j)
            {
                scanf("%d",&k);
                w[k][i] -= j;
            }
        }
        for (i = 1; i <= n; ++i)
        {
            for (j = 0; j < n; ++j)
            {
                scanf("%d",&k);
                w[i][k] -= j;
            }
        }
        printf("Data Set %d, Best average difference: %.6lf\n",cas++,KM()/(2.0*n));
        /*
          在完备匹配的相等子图上搜索所有结果;
        */
        cnt = 1;
        DfsRes(1);
        printf("\n");
    }
    return 0;
}

  

 

 

posted @ 2012-07-22 17:13  E_star  阅读(354)  评论(0编辑  收藏  举报