poj 3686 The Windy's( KM算法 )

恩 , 题是好题 , 一道经典KM算法题 , 只是刚开始读错题了,感觉思路没错但就是过不了样例,受不了了就上网查了一下题意,果然题意理解错了 ,改了一下输入,又加了个标记数组就这样的过了~  唉,由此可以看出学好英语是多么的重要!

题目大意是:有N个订单要在M个机器上加工,有一个N*M的矩阵描述其加工时间,同一时间内每台机器只能加工一个订单,问加工完所有订单后,时的平均时间最小。

思路就是:

将订单作为二分图中X部的点,总共N个。

将每个机器拆成N个点作为二分图中Y部的点,总共N*M个。

第J个机器的第P个点代表,使用机器 J进行倒数第P次加工。

假设我们按顺序在J机器上工件i1 , i2 , i3.....ik个工件,则总共需要花费i1*k + i2*(k-1) + i3*(k-2) +......+ ik*1;

所以我们对于X中每个点I,Y中每个点(J,P),连接一条hour[I,J]*P权值的边。

接下来进行二分图最佳匹配即可。

不过还是有收获的,至少对KM算法有了一点新的认识,KM算法类似于求图的最短路,只不过这个图要是二分图,并且两个点之间不能有其他的点,然后就是和求最短路一样的套用模板了。

代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#define maxx 55
#define INF 0xffffff
using namespace std;

int mm[maxx][maxx*maxx] , hour[maxx][maxx] ;
int lx[maxx] , ly[maxx*maxx] ;
int vistx[maxx] , visty[maxx*maxx] ;
int linkx[maxx*maxx] , linky[maxx*maxx] ;
int N , M ;

void init()
{
    int i , j , k ;
    
    memset( hour , 0 , sizeof ( hour ));
    memset( mm , 0 , sizeof ( mm ));
    
    for ( i = 0 ; i < N ; i++ )
    for ( j = 0 ; j < M ; j++ )
    scanf ( "%d", &hour[i][j] );
    
    for ( i = 0 ; i < N ; i++ )//处理输入,将M台机器每台分为N份
    for ( j = 0 ; j < M ; j++ )
    for ( k = 0 ; k < N ; k++ )
    mm[i][j*N+k] = hour[i][j] * ( k+1 );
    
    M = N * M ;//二分图中Y集中共有N*M个点
    
    memset( lx , 127 , sizeof ( lx ));//将X集中点的顶标置为最大
    memset( ly , 0 , sizeof ( ly ));
    
    for ( i = 0 ; i < N ; i++ )
    for ( j = 0 ; j < M ; j++ )
    if ( mm[i][j] < lx[i] )
    lx[i] = mm[i][j] ;//初始化X集中点的顶标
}

int find ( int x )
{
    int i ;
    vistx[x] = 1;
    for ( i = 0 ; i < M ; i++ )
    if ( !visty[i] && lx[x] + ly[i] == mm[x][i] )
    {
        visty[i] = 1 ;
        if ( linkx[i] == -1 || find ( linkx[i] ))
        {
            linkx[i] = x;
            linky[x] = i ;
            return 1;
        }
    }
    return 0;
}

void KM()
{
    int i  , j , k ;
    
    memset( linkx , -1 , sizeof ( linkx ));
    memset( linky , -1 , sizeof ( linky ));
    
    for ( i = 0 ; i < N ; i++ )
    {

        while ( 1 )
        {
            memset( vistx , 0 , sizeof ( vistx ));
            memset( visty , 0 , sizeof ( visty ));
            if ( find ( i ))//判断是否为完美匹配
            break;
            int minn = INF ;
            for ( j = 0  ; j < N ; j++ )
            if ( vistx[j] )
            {
                for ( k = 0 ; k < M ; k++ )
                if ( !visty[k] && mm[j][k] - lx[j] - ly[k] < minn )
                minn = mm[j][k] - lx[j] - ly[k] ;
            }
            for ( j = 0 ; j < N ; j++ )//调整顶标
            if ( vistx[j] )
            lx[j] += minn ;
            for ( j = 0 ; j < M ; j++ )
            if ( visty[j] )
            ly[j] -=minn ;
        }
    }
    
    int sum = 0;
    for ( i = 0 ; i < N ; i++ )
    sum += mm[i][linky[i]];
    //cout<<sum<<endl;
    printf ( "%.6lf\n" , 1.0 * sum / N );
}

int main()
{
    int cas ;

    scanf ( "%d" , &cas );
    while ( cas-- )
    {
        scanf ( "%d%d" , &N , &M );
        init();
        KM();
    }
    return 0;
}

 

posted @ 2012-05-19 16:42  Misty_1  阅读(222)  评论(0编辑  收藏  举报