当前位置:嗨网首页>书籍在线阅读

25-伪代码详解

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

7.4.4 伪代码详解

(1)定义结构体

结构体的定义和7.3.6节中改进算法ISAP中的结构体相同,边仅多了一个cost域。first指向第一个邻接边,next是下一条邻接边。该结构体用于创建邻接表。

struct Vertex    //邻接表头结点
{
    int first; 
}V[N]; 
struct Edge      //边结点
{
    int v, next; //v为弧头,next指向下一条邻接边
    int cap, flow,cost; 
}E[M];

(2)创建残余网络边

正向边的容量为cap,流量为0,费用为cost,反向边容量为0,流量为0,费用为−cost。

void add_edge(int u, int v, int c,int cost) //创建边
{
     E[top].v = v; 
     E[top].cap = c; 
     E[top].flow = 0; 
     E[top].cost = cost; 
     E[top].next = V[u].first; 
     V[u].first = top++;
}
void add(int u,int v, int c,int cost)       //添加两条边,正向边和反向边
{
     add_edge(u,v,c,cost); 
     add_edge(v,u,0,-cost); 
}

(3)求最小费用路

先初始化每个结点的距离为无穷大,然后令源点的距离dist[v1]=0。在混合网络中,从源点出发,沿可行边(E[i].cap>E[i].flow)广度搜索每个邻接点,如果当前距离dist[v]>dist[u]+ E[i].cost,则更新为最短距离dist[v]=dist[u]+E[i].cost,并记录前驱。

bool SPFA(int s, int t, int n)      //求最小费用路的SPFA
{
     int i, u, v; 
     queue <int> qu;                //队列
     memset(vis,false,sizeof(vis)); //访问标记初始化
     memset(c,0,sizeof(c));         //入队次数初始化
     memset(pre,-1,sizeof(pre));    //前驱初始化
     for(i=1;i<=n;i++)
     {
          dist[i]=INF;              //距离初始化
     }
     vis[s]=true;                   //结点入队vis要做标记
     c[s]++;                        //要统计结点的入队次数
     dist[s]=0; 
     qu.push(s); 
     while(!qu.empty())
     {
          u=qu.front();
          qu.pop();
          vis[u]=false; 
          //队头元素出队,并且消除标记
          for(i=V[u].first; i!=-1; i=E[i].next)//遍历结点u的邻接表
          {
               v=E[i].v; 
               if(E[i].cap>E[i].flow && dist[v]>dist[u]+E[i].cost)//松弛操作
               {
                     dist[v]=dist[u]+E[i].cost; 
                     pre[v]=i;      //记录前驱
                     if(!vis[v])    //结点v不在队内
                     {
                            c[v]++; 
                            qu.push(v);        //入队
                            vis[v]=true;       //标记
                            if(c[v]>n)         //超过入队上限,说明有负环
                                  return false; 
                     }
               }
          }
     }
     cout<<"最短路数组"<<endl; 
     cout<<"dist[ ]="; 
     for(int i=1;i<=n;i++)
         cout<<"  "<<dist[i]; 
     cout<<endl; 
     if(dist[t]==INF) 
          return false;     //如果距离为INF,说明无法到达,返回false
     return true; 
}

(4)沿着最小费用路增流

从汇点逆向到源点,找最小可增流量d=min(d, E[i].cap−E[i].flow)。沿着增广路径正向边增流d,反向边减流d,产生的费用为mincost+=dist[t]*d。

int MCMF(int s,int t,int n) //minCostMaxFlow
{
     int d;                 //可增流量
     int i,mincost;         //maxflow 当前最大流量,mincost当前最小费用
     mincost=0; 
     while(SPFA(s,t,n))     //表示找到了从s到t的最小费用路
     {
          d=INF; 
          cout<<endl; 
          cout<<"增广路径:"<<t; 
          for(i=pre[t]; i!=-1; i=pre[E[i^1].v]) //从汇点逆向沿增广路找最小可增量
          {
               d=min(d, E[i].cap-E[i].flow);    //找最小可增流量
               cout<<"--"<<E[i^1].v; 
          }
          cout<<"增流:"<<d<<endl; 
          cout<<endl; 
          maxflow+=d;       //更新最大流
          for(i=pre[t]; i!=-1; i=pre[E[i^1].v])   //增广路上正向边流量+d,反向边流量-d
          {
               E[i].flow+=d; 
               E[i^1].flow-=d; 
          }
          mincost+=dist[t]*d; //dist[t]为该路径上单位流量费用之和,最小费用更新
     }
     return mincost; 
}