poj 3686 The Windy's

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;

}



 

你可能感兴趣的:(poj)