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

70-实战演练

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

7.10.5 实战演练

//program 7-9
#include<iostream>
#include<cstring>
#include<map>
#include <queue>
using namespace std;
#define INF 1000000000
#define M 150
#define N 10000
int top;            //当前边下标
int dist[N], pre[N];//dist[i]表示源点到点i最短距离,pre[i]记录前驱
bool vis[N];        //标记数组
int c[N];           //入队次数
int maxflow,mincost;//maxflow 当前最大流量,mincost当前最小费用
string str[M];
map<string,int> maze;
struct Vertex
{
    int first;
}V[N];
struct Edge
{
    int v, next;
    int cap, flow,cost;
}E[M];
void init()
{
     memset(V, -1, sizeof(V));
     top=0;
}
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);
}
bool SPFA(int s, int t, int n)   //求最小费用路的SPFA
{
     int i, u, v;
     queue <int> qu;             //队列,STL实现
     memset(vis,0,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;
}
int MCMF(int s,int t,int n) //minCostMaxFlow
{
     int d;                 //可增流量
     maxflow=mincost=0;     //maxflow 当前最大流量,mincost当前最小费用
     while(SPFA(s,t,n))     //表示找到了从s到t的最短路
     {
          d=INF;
          cout<<endl;
          cout<<"增广路径:"<<t;
          for(int 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;
          for(int i=pre[t]; i!=-1; i=pre[E[i^1].v])//修改混合网络,增加增广路上相应弧的容量,并减少其反向边容量
          {
                 E[i].flow+=d;
                 E[i^1].flow-=d;
          }
          maxflow+=d;         //更新最大流
          mincost+=dist[t]*d; //dist[t]为该路径上单位流量费用之和 ,最小费用更新
    }
    return maxflow;
}
void print(int s,int t)
{
     int v;
     vis[s]=1;
     for(int i=V[s].first;~i;i=E[i].next)
         if(!vis[v=E[i].v]&&((E[i].flow>0&&E[i].cost<=0)||(E[i].flow<0&&E[i]. cost>=0)))
         {
             print(v,t);
             if(v<=t)
                cout<<str[v]<<endl;
         }
}
int main()
{
     int n,m,i;
     string str1,str2;
     cout<<"请输入景点个数n和直达线路数m:"<<endl;
     cin>>n>>m;
     init();                  //初始化
     maze.clear();
     cout<<"请输入景点名str"<<endl;
     for(i=1;i<=n;i++)
     {
         cin>>str[i];
         maze[str[i]]=i;
         if(i==1||i==n)
             add(i,i+n,2,0);
         else
             add(i,i+n,1,0);
     }
     cout<<"请输入可以直达的两个景点名str1,str2"<<endl;
     for(i=1;i<=m;i++)
     {
          cin>>str1>>str2;
          int a=maze[str1],b=maze[str2];
          if(a<b)
          {
             if(a==1&&b==n)
                 add(a+n,b,2,-1);
             else
                 add(a+n,b,1,-1);
          }
          else
          {
                if(b==1&&a==n)
                   add(b+n,a,2,-1);
                else
                   add(b+n,a,1,-1);
           }
     }
     if(MCMF(1,2*n,2*n)==2)
     {
        cout<<"最多经过的景点个数:"<<-mincost<<endl;
        cout<<"依次经过的景点:"<<endl;
        cout<<str[1]<<endl;
        memset(vis,0,sizeof(vis));//访问标记初始化
        print(1,n);
        cout<<str[1]<<endl;
     }
     else
        cout<<"No Solution!"<<endl; 
     return 0; 
}

算法实现和测试

(1)运行环境

Code::Blocks

(2)输入

请输入景点个数n和直达线路数m:
8 10
请输入景点名str
Zhengzhou
Luoyang
Xian
Chengdu
Kangding
Xianggelila
Motuo
Lasa
请输入可以直达的两个景点名str1;str2
Zhengzhou Luoyang
Zhengzhou Xian
Luoyang Xian
Luoyang Chengdu
Xian Chengdu
Xian Xianggelila
Chengdu Lasa
Kangding Motuo
Xianggelila Lasa
Motuo Lasa

(3)输出

最多经过的景点个数:6
依次经过的景点:
Zhengzhou
Luoyang
Chengdu
Lasa
Xianggelila
Xian
Zhengzhou