Optimal Milking-最大流/FordFulkerson/Dinic

4 篇文章 0 订阅
3 篇文章 0 订阅
Optimal Milking
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;
}



源代码二(Floyed+二分+Dinic):
#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更新流量。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值