poj 3686 The Windy's 二分匹配 KM算法求最小权匹配

              题意:有M个工作间,N个玩具订单,求最小的平均完成时间

             思路:对M个工作间分成N个点,j的第p个点表示倒数第p次加工,若要在一个工作间上完成k个订单则需要假设我们按顺序在J机器上工件I1,I2,I3..IK个工件,则总共需要花费I1*K+I2*(K- 1)+I3*(K-3)++IK, 所以我们对于X中每个点I,Y中每个点(J,P),连接一条A[I,J]*P权值的边.

       代码:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#define min(a,b) a<b?a:b
#define max(a,b) a>b?a:b
#define INF 1e15
#define maxn 60
#define clr(a,b) memset(a,b,sizeof(a))

using namespace std;
int sx[maxn],sy[maxn*maxn];
int lx[maxn],ly[maxn*maxn],mat[maxn*maxn];
int martix[maxn][maxn],ca,m,n;
int w[maxn][maxn*maxn];
void init()
{
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
          for(int p=1;p<=n;p++)
            w[i][(j-1)*n+p]=-martix[i][j]*p;
}
bool find(int x)
{
    sx[x]=1;
    for(int i=1;i<=n*m;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()
{
    for(int i=1;i<=n*m;i++) ly[i]=0;
        for(int i=1;i<=n;i++)
       {
           lx[i]=-INF;
           for(int j=1;j<=n*m;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 k=1;k<=n;k++)
              if(sx[k])
              for(int j=1;j<=n*m;j++)
               if(!sy[j])
                d=min(d,lx[k]+ly[j]-w[k][j]);
            for(int k=1;k<=n;k++) if(sx[k]) lx[k]-=d;
            for(int k=1;k<=m*n;k++) if(sy[k]) ly[k]+=d;
       }
    int sum=0;
    for(int i=1;i<=n*m;i++)
    {
        if(mat[i]==-1)
           continue;
        sum+=w[mat[i]][i];
    }
    return -sum;
}
int main()
{
    scanf("%d",&ca);
    while(ca--)
    {
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;i++)
          for(int j=1;j<=m;j++)
             scanf("%d",&martix[i][j]);
        init();
        printf("%lf\n",KM()/(1.0*n));
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值