poj3686(KM算法求解)

The Windy’s
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 5914 Accepted: 2457
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

网上看的题解,挺巧妙地,有N个工件要在M个机器上加工,有一个N*M的矩阵描述其加工时间。
同一时间内每个机器只能加工一个工件,问加工完所有工件后,使得平均加工时间最小(等待的时间+加工的时间)。(这个题目中没给,很坑,得自己理解)

假设某个机器处理了k个玩具,时间分别为a1,a2…..,ak

那么该机器耗费的时间为a1+(a1+a2)+(a1+a2+a3)…….(a1+a2+…ak)

即a1*k + a2 * (k - 1) + a3 * (k - 2)…. + ak

ai玩具在某个机器上倒数第k个处理,所耗费全局的时间为ai*k

对每个机器,最多可以处理n个玩具,拆成n个点,1~n分别代表某个玩具在这个机器上倒数第几个被加工的,对于每个玩具i,机器j中拆的每个点k,连接一条w[i][j]*k权值的边

KM算法讲解:https://blog.csdn.net/c20180630/article/details/71080521
https://www.cnblogs.com/wenruo/p/5264235.html

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define MAXN 55
#define inf 0x3f3f3f3f
//理解这个算法,比较重要的点是wx初始化存的是所有连的边的最大值
//每一次找增广路径,找不到的时候,通过减d(就是当前相等子图内顶标和与边差值最小的)
int dis[MAXN][MAXN * MAXN];
int linky[MAXN * MAXN];//存储y集合里面的匹配的x集合点
int wx[MAXN],wy[MAXN * MAXN];//存储km算法里的顶标(初始化的时候,wx存的是与自身连的边里面最大的边)
int visx[MAXN],visy[MAXN * MAXN];//每次找当前x集合点的增广路径时候,初始化一次
int minz;//存的是每次的顶标和与边的差值的最小值
int n,m;

bool dfs(int s)
{
    visx[s] = true;
    for(int i = 1;i <= m * n;++i)
    {
        if(!visy[i]){
            int t = wx[s] + wy[i] - dis[s][i];
            if(t == 0){
                visy[i] = true;
                if(linky[i] == -1 || dfs(linky[i])){
                    linky[i] = s;
                    return true;
                }
            }
            else{
                if(t > 0){
                    if(minz > t){
                        minz = t;
                    }
                }
            }
        }
    }
    return false;
}

int KM()
{
    memset(linky,-1,sizeof(linky));
    memset(wy,0,sizeof(wy));
    for(int i = 1;i <= n;++i)
    {
        wx[i] = -1 * inf;
        for(int j = 1;j <= m * n;++j)
        {
            if(dis[i][j] > wx[i]){
                wx[i] = dis[i][j];
            }
        }
    }
    for(int i = 1;i <= n;++i)
    {
        while(1)
        {
            minz = inf;
            memset(visx,false,sizeof(visx));
            memset(visy,false,sizeof(visy));
            if(dfs(i)) break;
            //对相同子图内的点进行加减操作
            for(int j = 1;j <= n;++j)
            {
                if(visx[j]){
                    wx[j] -= minz;
                }
            }
            for(int j = 1;j <= m * n;++j)
            {
                if(visy[j]){
                    wy[j] += minz;
                }
            }
        }
    }
    int ans = 0;
    for(int i = 1;i <= m * n;++i)
    {
        if(linky[i] != -1){
            ans += dis[linky[i]][i];
        }
    }
    return -ans;
}

void init()
{
        scanf("%d %d",&n,&m);
        memset(dis,-inf,sizeof(dis));
        for(int i = 1;i <= n;++i)
        {
            int cnt = 1;
            for(int j = 1;j <= m;++j)
            {
                int cost;
                scanf("%d",&cost);
                for(int k = 1;k <= n;++k)
                {
                    dis[i][cnt++] = -1 * cost *  k;
                }
            }
        }
}

int main()
{
    int t;
    scanf("%d",&t);
        for(int i = 0;i < t;++i){
            init();
            double ans = (KM() * 1.0) / n;
            printf("%.6f\n",ans);
        }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36386435/article/details/80113572