首页 > 代码库 > [ACM] POJ 3686 The Windy's (二分图最小权匹配,KM算法,特殊建图)

[ACM] POJ 3686 The Windy's (二分图最小权匹配,KM算法,特殊建图)

The Windy‘s
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 4158 Accepted: 1777

Description

The Windy‘s is a world famous toy factory that owns M top-class workshop to make toys. This year the manager receivesN orders for toys. The manager knows that every order will take different amount of hours in different workshops. More precisely, thei-th order will take Zij hours if the toys are making in thej-th workshop. Moreover, each order‘s work must be wholly completed in the same workshop. And a workshop can not switch to another order until it has finished the previous one. The switch does not cost any time.

The manager wants to minimize the average of the finishing time of the N orders. Can you help him?

Input

The first line of input is the number of test case. The first line of each test case contains two integers,N andM (1 ≤ N,M ≤ 50).
The next N lines each contain M integers, describing the matrixZij (1 ≤Zij ≤ 100,000) There is a blank line before each test case.

Output

For each test case output the answer on a single line. The result should be rounded to six decimal places.

Sample Input

3

3 4
100 100 100 1
99 99 99 1
98 98 98 1

3 4
1 100 100 100
99 1 99 99
98 98 1 98

3 4
1 100 100 100
1 99 99 99
98 1 98 98

Sample Output

2.000000
1.000000
1.333333

Source

POJ Founder Monthly Contest – 2008.08.31, windy7926778

 

解题思路:

 

题意为有n个订单,m个工厂,第i个订单在第j个工厂生产的时间为t[i][j],一个工厂可以生产多个订单,但一次只能生产一个订单,也就是说如果先生产a订单,那么b订单要等到a生产完以后再生产,问n个订单用这m个工厂全部生产完需要最少的时间是多少。

思路转载于:http://blog.csdn.net/lin375691011/article/details/19292473

这个题在建图上有一些需要思考很长时间的地方。因为每个订单所消耗的时间是车间完成订单的时间加上订单等待的时间。我们设在车间A需要完成k个订单,消耗的总时间是t1+(t1+t2)+(t1+t2+t3)……转换一下就是t1*k+t2*(k-1)+t3*(k-3)……我们就找到了规律:当第i个订单在第j个车间是倒数第k个任务时,总消耗时间需要加上订单i在车间对应消耗时间的k倍。

补充:也就是说把m个工厂看作m个点,每个点又拆成n个点(因为每个工厂最多可以生产n个订单),拆成的第i个点代表某一个订单在该工厂里面是倒数第i个被生产的。

g[i][k*n+j]=-t[i][k]*(j+1);//第i个订单在第k个工厂生产,且在第k个工厂中生产的所有订单中它排在倒数第j位被生产

0<=i<n ,   0<=k<m ,  0<=j<n

如图:

KM算法是求找出n条边所获得的最大权值,而在本题中要求最小权值,把所用时间取反,那么利用Km算法所求的最大权值的相反数就是我们所要求的最小权值。

代码:

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <iomanip>
using namespace std;
const int maxn=52;
const int inf=0x3f3f3f3f;
int n,m;
int t[maxn][maxn];
int nx,ny;
int g[maxn][maxn*maxn];
int linked[maxn*maxn],lx[maxn],ly[maxn*maxn];
int slack[maxn*maxn];
bool visx[maxn],visy[maxn*maxn];

bool DFS(int x)
{
    visx[x]=true;
    for(int y=0;y<ny;y++)
    {
        if(visy[y])
            continue;
        int tmp=lx[x]+ly[y]-g[x][y];
        if(tmp==0)
        {
            visy[y]=true;
            if(linked[y]==-1||DFS(linked[y]))
            {
                linked[y]=x;
                return true;
            }
        }
        else if(slack[y]>tmp)
            slack[y]=tmp;
    }
    return false;
}

int KM()
{
    memset(linked,-1,sizeof(linked));
    memset(ly,0,sizeof(ly));
    for(int i=0;i<nx;i++)
    {
        lx[i]=-inf;
        for(int j=0;j<ny;j++)
            if(g[i][j]>lx[i])
            lx[i]=g[i][j];
    }
    for(int x=0;x<nx;x++)
    {
        for(int i=0;i<ny;i++)
            slack[i]=inf;
        while(true)
        {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            if(DFS(x))
                break;
            int d=inf;
            for(int i=0;i<ny;i++)
                if(!visy[i]&&d>slack[i])
                d=slack[i];
            for(int i=0;i<nx;i++)
                if(visx[i])
                lx[i]-=d;
            for(int i=0;i<ny;i++)
            {
                if(visy[i])
                    ly[i]+=d;
                else
                    slack[i]-=d;
            }
        }
    }
    int ans=0;
    for(int i=0;i<ny;i++)
        if(linked[i]!=-1)
        ans+=g[linked[i]][i];
    return -ans;
}

int main()
{
    int cas;
    cin>>cas;
    while(cas--)
    {
        cin>>n>>m;
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
            cin>>t[i][j];
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                for(int k=0;k<m;k++)
                    g[i][k*n+j]=-t[i][k]*(j+1);//第i个订单在第k个工厂生产,且在第k个工厂中生产的所有订单中它排在倒数第j位被生产
        m=n*m;
        nx=n;ny=m;
        cout<<setiosflags(ios::fixed)<<setprecision(6)<<1.0*KM()/n<<endl;
    }
    return 0;
}


 

 

 

 

 

 

[ACM] POJ 3686 The Windy's (二分图最小权匹配,KM算法,特殊建图)