恩 , 题是好题 , 一道经典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; }