poj3686-The Windy's (km算法)(建图有点难想)

The Windy's
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 5763 Accepted: 2380

Description

The Windy's is a world famous toy factory that owns M top-class workshop to make toys. This year the manager receives N orders for toys. The manager knows that every order will take different amount of hours in different workshops. More precisely, the i-th order will take Zij hours if the toys are making in the j-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 and M (1 ≤ N,M ≤ 50).
The next N lines each contain M integers, describing the matrix Zij (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



题目大意:

有n个订单m个车间,每个车间均可以单独完成任何一个订单。每个车间完成不同订单的时间是不同的。不会出现两个车间完成同一个订单的情况。给出每个订单在某个车间完成所用的时间。问订单完成的平均时间是多少。


参考博客: http://www.mamicode.com/info-detail-288927.html


解题思路:

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

2、建图的时候记得用负值来建图,这样顶标数组初始化成0就行了。因为不会有比0大的值出现。

3、结果出来后取相反数,然后除以n来得到最后的结果。


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



因为km算法是求最大权值的完备匹配,而在本题中要求最小权值,把权值地取为负数,那么利用Km算法所求的最大权值的相反数就是我们所要求的最小权值。



代码:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
using namespace std;
#define INF 0x3f3f3f
const int maxn=55;
int n,m;
int nx,ny;
int lx[maxn],ly[maxn*maxn],match[maxn*maxn],slack[maxn*maxn];
bool visx[maxn],visy[maxn*maxn];
int edge[maxn][maxn*maxn],s[maxn][maxn];
int max(int a,int b)
{
    if(a>b)
        return a;
    else
        return b;
}
int min(int a,int b)
{
    if(a<b)
        return a;
    else
        return b;
}
bool dfs(int x)
{
    visx[x]=true;
    for(int y=0;y<ny;y++)
    {
        if(visy[y])
            continue;
        int tmp=lx[x]+ly[y]-edge[x][y];
        if(tmp==0)
        {
            visy[y]=true;
            if(match[y]==-1||dfs(match[y]))
            {
                match[y]=x;
                return true;
            }
        }
        else{
            slack[y]=min(slack[y],tmp);
        }
    }
    return false;
}
int KM()
{
    memset(match,-1,sizeof(match));
    memset(ly,0,sizeof(ly));
    for(int i=0;i<nx;i++)
    {
        lx[i]=-INF;
        for(int j=0;j<ny;j++)
        {
            lx[i]=max(lx[i],edge[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 = min(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(match[i]!=-1)
            ans+=edge[match[i]][i];
    }
    return -ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                scanf("%d",&s[i][j]);
            }
        }
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                for(int k=0;k<m;k++)
                {
                    edge[i][k*n+j]=-s[i][k]*(j+1);//第i个订单在第k个工厂生产,且在第k个工厂中生产的所有订单中它排在倒数第j位被生产
                    //因为这里是求最小值,而km算法是求最大权值的,所以要取负号
                }
            }
        }
        m*=n;
        nx=n;
        ny=m;
        //printf("%d\n",KM());
        printf("%.6lf\n",1.0*KM()/n);
    }
    return 0;
}
km模板可以看一下这个博客(这个大佬讲得挺好理解的):
https://www.cnblogs.com/wenruo/p/5264235.html



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值