码迷,mamicode.com
首页 > 编程语言 > 详细

最大流最小割C++实现

时间:2014-06-21 09:18:08      阅读:1088      评论:0      收藏:0      [点我收藏+]

标签:style   class   blog   code   color   使用   

最大流量问题:寻找从源点到汇点的最大流量。

使用最短增益路径法(或先标记先扫描法)。这里标记意味着用两个记号来标记一个新的(为标记)的顶点:第一个标记指出从源点到被标记顶点还能增加多少流量;第二用来分别个标记指出另一个顶点的名字,就是从该顶点访问到被标记顶点的(对于源点来说这个标记可以不必指定)。方便起见,也可以为第二个标记加上“+”或“-”符号,用来分别指出该顶点是通过前向边还是后向边访问到的。因此,源点可被标记为∞,-。对于其他顶点,按照以下方法计算它的标记:

(1)如果为标记顶点j是由j到i的有向边和遍历队列中的前面顶点i相连接的,而且j具有大于0的未使用容量rij=uij-xij,其中uij为边的总容量,xij为当前正容量,那么顶点j就标记为lj,i+,其中lj=min{li,rij}。

(2)如果为标记顶点j是由j到i的有向边和遍历队列中的前面顶点i相连接的,而且j具有大于0的未使用容量xji,那么顶点j就标记为lj,i-,其中lj=min{li,xji}。

如果这种加入了标记的遍历结束于汇点,当前流量的增量就可以确定为汇点的第一个标记。我们从汇点开始,沿着第二个标记回溯到源点,来执行这一增量。在这条路径上,前向边的当前流量增加,后向边的当前流量减少。另一方面,如果遍历队列为空以后,汇点仍然没有被标记,该算法就把当前流量作为最大值返回并结束算法。

 

最小割

首先说明什么是割——我们可以把网络的顶点分成两个子集X和X‘,X包含源点,X‘是X的部,包含汇点。所有头在X‘,尾在X的边的集合称作,记作C(X,X‘)或C。

割C(X,X‘)容量,记作c(X,X‘),定义为构成割的边的容量和。最小割是具有最小容量的割。

最大流-最小割定理:网络中的最大流量值等于它的最小割的容量。

在最大流为问题的最后一次迭代时,所有已标记顶点到未标记顶点的边就构成了最小割。

 

注:以上文字摘自《算法设计与分析基础》,(美)Anany Levitin 著,潘彦 译。

 

#include<iostream>
#include<queue>
using namespace std;

const int MAX=100;
const int inf=1<<30;
queue<int> Q;

int ShortestAugmentingPath(int n,int source,int sink,int map[][MAX],int flow[][MAX],int pre_t[])
{
    /*  n为顶点数 ,source为源点,sink为汇点,map[][]记录对应边的最大容量,flow[][]记录对应边具有的正流量;
    最后一次迭代的pre_t[]记录了最小割,pre_t[i] !=0 表示属于源点集合  */
    
    if(source==sink)  //特殊情况处理
        return inf;
    
    int i,x;
    int visited[MAX];    //visited[i]=1表示顶点i被标记
    int rest[MAX][MAX];  //rest[i][j]表示顶点i到j的未使用流量
    int L[MAX];   //L[i]记录一次迭代过程中节点i的最大增量
    int pre[MAX]; //pre[i]=0表示顶点i未标记,pre[i] >0 表示前向边,pre[i] < 0表示后向边

   //数组初始化
    memset(rest,0,sizeof(rest));
    memset(visited,0,sizeof(visited));
    memset(flow,0,(n+1)*sizeof(flow[0]));
    memset(L,0x7f,n*sizeof(L[0]));      
    memset(pre,0,sizeof(pre));   
    
    pre[source]=source+1;  //任意赋值为一个整数
    
    while(!Q.empty())
        Q.pop();
    Q.push(source);  //最初,源点入队
    
    while(!Q.empty())
    {
        visited[source]=1;  //源点被标记
        i=Q.front();
        Q.pop();
        
        for(x=1;x<=n;x++) //前向边
        {                                      
            if(i!=x&&!visited[x]&&map[i][x]!=inf)
            {
                rest[i][x]=map[i][x]-flow[i][x];
                
                if( rest[i][x] > 0 )
                {
                    L[x]=L[i] < rest[i][x] ? L[i] : rest[i][x]; //L[x]=min(rest[i][x],L[i])
                    Q.push(x);
                    visited[x]=1;
                    pre[x]=i;
                }
            }
        }

        for(x=1;x<=n;x++)  //后向边
        {                                        
            if(i!=x&&!visited[x]&&map[x][i]!=inf)
            {
                if(flow[x][i] > 0)
                {
                    L[x]=L[i] < flow[x][i] ? L[i] : flow[x][i];
                    Q.push(x);
                    visited[x]=1;
                    pre[x]=-i;
                }
            }
        }
        
        for(int y=1;y<=n;y++)  //记录最后一次迭代结果的标记,即为最小割
            pre_t[y]=pre[y];
        
        if(visited[sink]==1) //汇点被标记了
        {
            x=sink;
            while(x!=source) //从汇点开始用第二个标记反向移动
            {
                if(pre[x]>0) 
                    flow[i][x] += L[sink];
                else
                    flow[i][x] -= L[sink];
                x=i;
                
                if(pre[i]>0 )
                    i=pre[i];
                else
                    i=-pre[i];
            }
            
            memset(visited,0,sizeof(visited)); //擦除标记
            memset(L,0x7f,n*sizeof(L[0]));
            
            while(!Q.empty()) 
                Q.pop();
            Q.push(source); //用源点对q重新初始化
            
            memset(pre,0,sizeof(pre));
            pre[source]=1;
        }
    }
    
    int maxflow=0;
    for(i=1; i <= n;i++)
        maxflow+=flow[i][sink];
    return maxflow;
}
    
int main()
{
    int map[MAX][MAX],flow[MAX][MAX],pre[MAX];
    int n,i,x,w;
    
    cout<<"输入顶点数:";
    while(cin>>n && n)
    {
        for(i=1;i<=n;i++)
        {
            for(x=1;x<=n;x++)
            map[i][x]=inf;
        }
        
        int m; //输入边数
        cout<<"输入边数:  ";
        cin>>m;
        
        cout<<"分别输入每个有向边的起点,终点和容量:"<<endl;
        while(m--)
        {
            cin>>i>>x>>w;
            map[i][x]=w;
        }
        cout<<"最大流是:"<<ShortestAugmentingPath(n,1,n,map,flow,pre)<<endl;
        
        cout<<"最小割为: 源点集合X={";
        for(i=1;i<=n;i++)
        {
            if(pre[i]!=0 )
                cout<<i<<" ";
        }
        cout<<"}, 汇点集合Y={";
        for(i=1;i<=n;i++)
        {
            if(pre[i]==0)
                cout<<i<<" ";
        }
        cout<<"}"<<endl;
    }
    return 0;
}

 

最大流最小割C++实现,布布扣,bubuko.com

最大流最小割C++实现

标签:style   class   blog   code   color   使用   

原文地址:http://www.cnblogs.com/littleTing/p/3795011.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!