Source:POJ-2112
Description
FJ has moved his K (1 <= K <= 30) milking machines out into the cow pastures among the C (1 <= C <= 200) cows. A set of paths of various lengths runs among the cows and the milking machines. The milking machine locations are named by ID numbers 1..K; the cow locations are named by ID numbers K+1..K+C.
Each milking point can "process" at most M (1 <= M <= 15) cows each day.
Write a program to find an assignment for each cow to some milking machine so that the distance the furthest-walking cow travels is minimized (and, of course, the milking machines are not overutilized). At least one legal assignment is possible for all input data sets. Cows can traverse several paths on the way to their milking machine.
Input
* Line 1: A single line with three space-separated integers: K, C, and M.
* Lines 2.. ...: Each of these K+C lines of K+C space-separated integers describes the distances between pairs of various entities. The input forms a symmetric matrix. Line 2 tells the distances from milking machine 1 to each of the other entities; line 3 tells the distances from machine 2 to each of the other entities, and so on. Distances of entities directly connected by a path are positive integers no larger than 200. Entities not directly connected by a path have a distance of 0. The distance from an entity to itself (i.e., all numbers on the diagonal) is also given as 0. To keep the input lines of reasonable length, when K+C > 15, a row is broken into successive lines of 15 numbers and a potentially shorter line to finish up a row. Each new row begins on its own line.
Output
A single line with a single integer that is the minimum possible total distance for the furthest walking cow.
Sample Input1
2 3 2
0 3 2 1 1
3 0 3 2 0
2 3 0 1 0
1 2 1 0 2
1 0 0 2 0
Sample Output1
2
Sample Input2
3 3 1
0 0 0 5 0 6
0 0 0 1 5 0
0 0 0 0 0 5
5 1 0 0 2 0
0 5 0 2 0 0
6 0 5 0 0 0
Sample Output2
5
源代碼一(Floyed+二分+Ford-Fulkerson):
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
queue <int> Q;
//採用常變量定義INF和MAX,比起#define錯誤率低(考慮括號匹配不正確的情況,因爲移位運算符優先級略低)
const int INF = 1<<29; //Floyd有兩條路徑相加的情況,如果INF定義爲1<<30的話肯能會溢出
const int MAX = 300;
int K , C , M , S , T , l , r , m , pre[MAX] , mark[MAX];
int map[MAX][MAX] , gragh[MAX][MAX];
//l/m/r是二分的左中右的值,S/T爲網絡流源點和匯點
//map是輸入的地圖,以及Floyed以後形成的路徑圖
//gragh是每一次進行Ford_Fulkerson算法求最大流的暫存圖
int Ford_Fulkerson( int axis );
void Floyed( void );
int bfs( void );
int upDate( void );
int binarySearch( void );
void formNewGragh( int axis );
int main( ){
int i , j;
while( cin>>K>>C>>M ){
for( i=1 ; i<=K+C ; i++ ){
for( j=1 ; j<=K+C ; j++ ){
cin>>map[i][j];
if( !map[i][j] ) //按照題意對圖進行處理,0的話表示到達不了
map[i][j] = INF;
}
}
Floyed( ); //Floyed求機器到牛(即牛到機器(雙向圖))的最短距離
cout<<binarySearch()<<endl; //二分查找最長路的最小值.最大的最小\最小的最大可使用二分算法
}
return 0;
}
void Floyed( void ){
int k , i , j;
l = INF;
r = -INF;
for( k=1 ; k<=K+C ; k++ ) //按照Floyed算法的描述,K必須在最外層
for( i=1 ; i<=K+C ; i++ )
for( j=1 ; j<=K+C ; j++ )
if( map[i][k]+map[k][j]<map[i][j] ){
map[i][j] = map[i][k] + map[k][j];
}
for( i=1 ; i<=K ; i++ ) //找出l\r的界限,代碼的l,r表示閉區間[l,r]
for( j=K+1 ; j<=K+C ; j++ ){
if( map[i][j]>r )
r = map[i][j];
if( map[i][j]<l )
l = map[i][j];
}
}
int binarySearch( void ){
int temp;
while( l <= r ){ //如果沒有等號的話不正確,因爲r=m-1,如果當前m最優,那麼r錯過了m的賦值
m = ( l + r ) / 2;
if( Ford_Fulkerson( m )==C ){
temp = m;
r = m - 1;
}
else
l = m + 1;
}
return temp; //記錄最優解
}
int Ford_Fulkerson( int axis ){ //axis爲軸的值
int sum;
sum = 0;
formNewGragh( axis ); //形成新的網絡流圖
while( bfs( ) ){ //尋找增廣路徑
sum += upDate( ); //補充流量
}
return sum;
}
void formNewGragh( int axis ){
int i , j;
S = 0; //源點標號
T = K + C + 1; //匯點標號
memset( gragh , 0 , sizeof( gragh ) ); //初始化gragh圖
for( i=1 ; i<=K ; i++ )
for( j=K+1 ; j<=K+C ; j++ )
if( map[i][j]<=axis )
gragh[i][j] = 1;
for( i=1 ; i<=K ; i++ ) //補充源點
gragh[S][i] = M;
for( i=K+1 ; i<=K+C ; i++ ) //補充匯點
gragh[i][T] = 1;
}
int bfs( void ){
int u , v;
memset( mark , 0 , sizeof( mark ) ); //每次需要memset清空
while( !Q.empty( ) )
Q.pop( );
Q.push( S );
mark[S] = 1;
pre[S] = -1;
while( !Q.empty( ) ){
u = Q.front( );
Q.pop( );
if( u==T )
return 1;
for( v=0 ; v<=T ; v++ ){
if( !mark[v] && gragh[u][v] ){
Q.push( v ); //壓入隊列
pre[v] = u; //記錄前驅,以便更新
mark[v] = 1; //標記
}
}
}
return 0;
}
int upDate( void ){
int u , v , min;
v = T;
min = INF;
while( pre[v]!=-1 ){ //尋找最短可憎流量
u = pre[v];
if( gragh[u][v]<min )
min = gragh[u][v];
v = u;
}
v = T;
while( pre[v]!=-1 ){ //更新流量
u = pre[v];
gragh[u][v] -= min; //正向減去
gragh[v][u] += min; //逆向加上
v = u;
}
return min;
}
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
queue <int> Q;
//採用常變量定義INF和MAX,比起#define錯誤率低(考慮括號匹配不正確的情況,因爲移位運算符優先級略低)
const int INF = 1<<29; //Floyd有兩條路徑相加的情況,如果INF定義爲1<<30的話肯能會溢出
const int MAX = 300;
int K , C , M , S , T , l , r , m , mark[MAX] , level[MAX];
int map[MAX][MAX] , gragh[MAX][MAX];
//l/m/r是二分的左中右的值,S/T爲網絡流源點和匯點
//map是輸入的地圖,以及Floyed以後形成的路徑圖
//gragh是每一次進行Ford_Fulkerson算法求最大流的暫存圖
int Dinic( int axis );
void Floyed( void );
int bfs( void );
int dfs( int u , int flow );
int binarySearch( void );
void formNewGragh( int axis );
int min( int a , int b ){ return a <= b ? a : b; }
int main( ){
int i , j;
while( cin>>K>>C>>M ){
for( i=1 ; i<=K+C ; i++ ){
for( j=1 ; j<=K+C ; j++ ){
cin>>map[i][j];
if( !map[i][j] ) //按照題意對圖進行處理,0的話表示到達不了
map[i][j] = INF;
}
}
Floyed( ); //Floyed求機器到牛(即牛到機器(雙向圖))的最短距離
cout<<binarySearch()<<endl; //二分查找最長路的最小值.最大的最小\最小的最大可使用二分算法
}
return 0;
}
void Floyed( void ){
int k , i , j;
l = INF;
r = -INF;
for( k=1 ; k<=K+C ; k++ ) //按照Floyed算法的描述,K必須在最外層
for( i=1 ; i<=K+C ; i++ )
for( j=1 ; j<=K+C ; j++ )
if( map[i][k]+map[k][j]<map[i][j] ){
map[i][j] = map[i][k] + map[k][j];
}
for( i=1 ; i<=K ; i++ ) //找出l\r的界限,代碼的l,r表示閉區間[l,r]
for( j=K+1 ; j<=K+C ; j++ ){
if( map[i][j]>r )
r = map[i][j];
if( map[i][j]<l )
l = map[i][j];
}
}
int binarySearch( void ){
int temp;
while( l <= r ){ //如果沒有等號的話不正確,因爲r=m-1,如果當前m最優,那麼r錯過了m的賦值
m = ( l + r ) / 2;
if( Dinic( m )==C ){
temp = m;
r = m - 1;
}
else
l = m + 1;
}
return temp; //記錄最優解
}
int Dinic( int axis ){ //axis爲軸的值
int sum = 0;
formNewGragh( axis );
while( bfs( ) ){
sum += dfs( S , INF );
}
return sum;
}
void formNewGragh( int axis ){
int i , j;
S = 0; //源點標號
T = K + C + 1; //匯點標號
memset( gragh , 0 , sizeof( gragh ) ); //初始化gragh圖
for( i=1 ; i<=K ; i++ )
for( j=K+1 ; j<=K+C ; j++ )
if( map[i][j]<=axis )
gragh[i][j] = 1;
for( i=1 ; i<=K ; i++ ) //補充源點
gragh[S][i] = M;
for( i=K+1 ; i<=K+C ; i++ ) //補充匯點
gragh[i][T] = 1;
}
int bfs( void ){
int u , v;
memset( mark , 0 , sizeof( mark ) ); //每次需要memset清空
memset( level , 0 , sizeof( level ) );
while( !Q.empty( ) )
Q.pop( );
Q.push( S );
mark[S] = 1;
level[S] = 0; //源點層次爲0
while( !Q.empty( ) ){
u = Q.front( );
Q.pop( );
if( u==T )
return 1;
for( v=0 ; v<=T ; v++ ){
if( !mark[v] && gragh[u][v] ){
Q.push( v ); //壓入隊列
mark[v] = 1; //標記
level[v] = level[u] + 1; //層次記錄
}
}
}
return 0;
}
int dfs( int u , int flow ){
int v , flowSum , sum;
if( u==T ) //遞歸終止條件
return flow;
for( sum=0 , v=S ; v<=T ; v++ ){
if( level[v]==level[u]+1 && gragh[u][v] ){ //bfs進行分層處理,速度明顯加快
flowSum = dfs( v , min( gragh[u][v] , flow ) ); //flowSum是從該點開始,流經匯點的流量
gragh[u][v] -= flowSum;
gragh[v][u] += flowSum;
flow -= flowSum; //下一個結點能流量爲flow-flowSum
sum += flowSum; //flowSum累計該點流出的總流量
}
}
return sum; //回溯,返回該結點流出的總流量
}
代碼分析:題目採用了兩種方法求解,大體的思路在代碼註釋中了,Dinic算法是FordFulkerson的加強版(一次性找多條增廣路),需要利用bfs求層,dfs更新流量。