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

拆点+KM算法

题目大意:

n个玩具在m台机器上完成所需时间个不同

一台机器只有完成一个玩具的的制作才能继续完成其它的

问你n给玩具完成制作最小平均时间

思路转自

http://blog.sina.com.cn/s/blog_6af663940100mw9t.html

此题构图很巧妙。设n个订单的执行时间分别为t1,t2…tn,则n个订单的总的执行时间是
t1*n+t2*(n-1)+t3*(n-2)+…+tn-1*2+tn。将每个机器j拆成n个点,第k个点表示倒数第k个订单在此机器上完成,连边权值为:tmp[i][j]*k。这样就转换成了求二分图最小权匹配的问题了。KM算法,把权值设为负值求最大权匹配

 

求的是最小平均时间 把时间转换为负的 就可以求最大匹配了

我个人直接用三维数组储存的,这样原来的右组就由一维变成了二维

其它的都一样了

代码及其注释:

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

using namespace std;
const int MAX=0x7ffffff;
const int N=51;
int paytime[N][N][N];//拆点后花费时间,
int a[N];//左组顶标
int b[N][N];//右组顶标
bool lv[N];//左组是否在交叉树内
bool rv[N][N];//右组是否在交叉树内
int n,m;
int f[N][N];//右组指向
bool dfs(int x)//匈牙利算法找匹配
{
    lv[x]=true;
    for(int i=1;i<=m;++i)
    {
        for(int j=1;j<=n;++j)
        {
            if(!rv[i][j]&&a[x]+b[i][j]==paytime[x][i][j])
            {
                rv[i][j]=true;
                if(f[i][j]==-1||dfs(f[i][j]))
                {
                    f[i][j]=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<=m;++j)
        {
            for(int l=1;l<=n;++l)
            {
                a[i]=max(a[i],paytime[i][j][l]);//左组顶标初始最大
            }
        }
    }

    memset(f,-1,sizeof(f));
    for(int w=1;w<=n;++w)
    {
        while(1)
        {
          memset(lv,false,sizeof(lv));
          memset(rv,false,sizeof(rv));
          if(dfs(w))//匹配的话直接退出循环找下一个 否则减d继续找
          break;
          int d=MAX;
          for(int i=1;i<=n;++i)
          {
            if(lv[i])
            {
                for(int j=1;j<=m;++j)
                {
                    for(int l=1;l<=n;++l)
                    {
                        if(!rv[j][l])
                        {
                            d=min(d,a[i]+b[j][l]-paytime[i][j][l]);//找最小变化量
                        }
                    }
                }
            }
          }
          for(int i=1;i<=n;++i)
          {
            if(lv[i])
            a[i]-=d;
          }
          for(int j=1;j<=m;++j)
          {
            for(int l=1;l<=n;++l)
            {
                if(rv[j][l])
                b[j][l]+=d;
            }
          }
        }
    }
    int sum=0;
    for(int j=1;j<=m;++j)
    {
        for(int l=1;l<=n;++l)
        {
            if(f[j][l]!=-1)
            sum-=paytime[f[j][l]][j][l];//最优匹配总值
        }
    }
    return sum;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&m);
        int k;
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=m;++j)
            {
                 scanf("%d",&k);
                 for(int l=1;l<=n;++l)
                 {
                     paytime[i][j][l]=-(k*l);//拆点
                 }
            }
        }
        printf("%.6f\n",1.0*(KM())/n);
    }
    return 0;
}

 

posted on 2012-06-10 16:22  夜->  阅读(199)  评论(0编辑  收藏  举报