小奥的学习笔记

  • Home
  • Learning & Working
    • Speech Enhancement Notes
    • Programming language
    • Computer & DL
    • MOOC
  • Life
    • Life Time
    • Thinking & Comprehension
    • Volunteer
    • Plan
    • Travel
  • Footprints
  • GuestBook
  • About
    • About Me
    • 个人履历
    • 隐私策略
  1. 首页
  2. Study-notes
  3. Programming language
  4. Algorithm
  5. 正文

算法笔记之线性规划网络流问题(5)

2019年3月8日 1114点热度 0人点赞 0条评论

最大收益问题

问题分析

经过分析,又根据最大流最小割定理,最大流的流值等于最小割容量。即:实验方案净收益=所有实验项目收益-最大流值。所以只需要求出最大流值即可、

算法设计

  1. 构建网络。根据输入的数据,添加源点和汇点,从源点s到每个实验项目Ei有一条有向边,容量是项目产生的收益pi。从每个实验仪器Ij到汇点t有一条有向边,容量是仪器费用cj,每个实验项目到该实验项目用到的仪器有一条有向边容量是
    ∞,创建混合网络。
  2. 基于ISAP算法求网络最大流。
  3. 输出最大收益及实验方案。最大收益实验方案就是最小割中的S集合去掉源点。在最大流对应的混合网络中,从选点开始,沿着cap>flow的边深度优先遍历,遍历到的结点就是S集合,即对应的实验项目和仪器就是选中的实验方案。

代码实现

  1. 输出最大收益方案
void DFS(int s)//深度搜索最大获益方案
{
   for(int i=V[s].first;~i;i=E[i].next)//读当前结点的邻接表
       if(E[i].cap>E[i].flow)
       {
           int u=E[i].v;
           if(!flag[u])
           {
               flag[u]=true;
               DFS(u);
           }
       }
}
void print(int m,int n)//输出最佳方案
{
   cout<<"----------最大获益方案如下:----------"<<endl;
   DFS(0);
   cout<<"选中的实验编号:"<<endl;
   for(int i=1;i<=m;i++)
       if(flag[i])
          cout<<i<<"  ";
   cout<<endl;
   cout<<"选中的仪器编号:"<<endl;
   for(int i=m+1;i<=m+n;i++)
      if(flag[i])
          cout<<i-m<<"  ";
}

  1. 构建混合网络
cout<<"请输入实验数m和仪器数n:"<<endl;
    cin>>m>>n;
    init();
    total=m+n;
    cout<<"请依次输入实验产生的效益和该实验需要的仪器编号(为0结束):"<<endl;
    for(int i=1;i<=m;i++)
    {
        cin>>cost;
        sum+=cost;
        add(0, i, cost);//源点到实验项目的边,容量为该项目效益
        while(cin>>num,num) //num为该项目需要的仪器编号
            add(i, m+num, INF);//实验项目到需要仪器的边,容量为无穷大
    }
    cout<<"请依次输入所有仪器的费用:"<<endl;
    for(int j=m+1;j<=total;j++)
    {
        cin>>cost;
        add(j, total+1, cost);//实验仪器到汇点的边,容量为实验仪器费用
    }

算法复杂度

时间复杂度:找到一条可增广路时间为O(V),最多会执行O(VE)次,因为关键边的总数为O(VE),因此总的时间复杂度为O(V2E),其中V为结点个数,E为边的数量。

空间复杂度O(V)。

方格取数问题

问题简介

从一个矩阵中选取一些数,要求满足任意两个数不相邻,使这些数的和最大。实际上就是讲矩阵中的数分为两部分,对矩阵中的点进行黑白着色。

问题分析

黑色方格作为一个集合X,白色方格作为一个集合Y,可以将一个图分为两部分,构成一个二分图。添加源点和汇点,从源点向黑色方格连一条边,容量为该黑色方格的权值,从白色方格向汇点连一条边,容量为该白色方格的权值,对于每一对相邻的黑白方格,从黑方格向白方格连一条边,容量为无穷大。

同样是求出最小割,选中方格的最大权值=所有方格权值之和-最小割容量。

算法设计

  1. 构建网络
  2. 求网络最大流。
  3. 输出选中物品的最大价值、物品选择方案。

选中物品的最大价值=所有物品价值之和-最大流值
注意:切割线切到的边容量是没选中的方格权值。

方格取数1

物品选择方案就是最小割中的S集合中的黑色方格和T集合中的白色方格。找到最小割后,从源点出发,沿着cap>flow的边DFS,遍历到的结点就是S集合,没有遍历到的结点就是T集合。输出S集合中的黑色方格和T集合中的白色方格。如上图所示。

算法详解

假设整个图如下图所示。

方格取数1

  1. 构建网络,根据输入的数据,绘制二分图,添加源点s和汇点t,从黑方格向白方格连一条边,容量为∞,创建网络。

方格取数3

  1. 在混合网络上用ISAP算法求网络最大流,找到如下6条增广路径。
    • 增广路径:10-6-9-0.增流5。
    • 增广路径:10-8-9-0.增流15。
    • 增广路径:10-4-7-0.增流34。
    • 增广路径:10-2-5-0.增流70。
    • 增广路径:10-2-3-0.增流21。
    • 增广路径:10-2-1-0.增流75。

方格取数4

切割线中被选中的就是红色圈圈的结点,选择的道路是从源点出发,按照cap>flow的结点(这里面只有7和9两个结点)去深度遍历,这就是S集合;1、3、5和2是T集合。
所以选中的黑点为7、9,白色为2。

代码实现

(1)构建网络

//创建混合网络
for(int i=1;i<=m;i++)
    for(int j=1;j<=n;j++)
    {
        if((i+j)%2==0)//染黑色
        {
            add(0,(i-1)*n+j,map[i][j]);//从源点到当前物品加边,容量为物品价值
            flag[(i-1)*n+j]=1;//染色
            //当前物品结点到四个相邻物品结点发出一条有向边,容量为无穷大
            for(int k=0;k<4;k++)
            {
                int x=i+dir[k][0];
                int y=j+dir[k][1];
                if(x<=m && x>0 && y<=n && y>0)
                    add((i-1)*n+j,(x-1)*n+y,INF);
            }
        }
        else
            //染白色,也就相当于flag为0,不需要专门的语句
        //从源点到当前物品加边,容量为物品价值
            add((i-1)*n+j,total+1,map[i][j]);
    }

(2)输出挑选物品的最大价值

cout<<"挑选物品的最大价值:"<<sum-Isap(0,total+1,total+2)<<endl;

(3)输出选中的物品编号。

void DFS(int s)
{
    //读取当前结点的邻接表
    for(int i=V[s].first;~i;i=E[i].next)
    {
        if(E[i].cap>E[i].flow)
        {
            int u=E[i].v;
            if(!dfsflag[u])
            {
                dfsflag[u]=true;
                DFS(u);
            }
        }
    }
}
//输出最佳方案
void print(int m,int n)
{
    cout<<"-----最佳方案:-----"<<endl;
    cout<<"选中物品编号<<endl;
    DFS(0);
    for(int i=1;i<=m*n;i++)
        if(())
}

算法复杂度

时间复杂度:找到一条可增广路时间为O(V),最多会执行O(VE)次,因为关键边的总数为O(VE),因此总的时间复杂度为O(V2E),其中V为结点个数,E为边的数量。

空间复杂度O(V)。

旅游路线问题

问题分析

给定一张地图,图中结点代表景点,边代表景点可以直达。现要求找到满足下面要求且途径景点最多的一条旅游线路:

  1. 从最东端起点出发,从东往西经过若干个景点到达最西端的景点,然后再从西向东回到家(可途径若干景点);
  2. 除起点外,任何景点只能访问1次。

算法设计

  1. 构建网络。根据输入的数据,按顺序对景点进行编号,即景点i对应结点i,对每个结点拆点,拆为两个结点i和i',且从i到i'连接一条边,边容量为1,单位流量费用为0;源点和终点拆点时,边的容量为2,单位流量费用为0;如果景点i到景点j可以直达,则从结点i'到结点j连接一条边,边的容量为1,单位流量费用为-1,创建混合网络。
  2. 求网络最小费用最大流。
  3. 求出最优的旅游线路。从源点出发,沿着flow>0且cost≤0的方向深度优先遍历,到达终点后,再沿着flow<0且cost≥0的方向深度优先遍历,返回源点。

代码实现

  1. 构建网络
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);
         }
    }
  1. 求网络最小费用最大流
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;
}
  1. 输出最优的旅游路线
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;
      }
}
本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 算法 线性规划 网络流
最后更新:2019年3月8日

yszhang

这个人很懒,什么都没留下

打赏 点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

搜索
欢迎关注我的个人公众号
最新 热点 随机
最新 热点 随机
DEEPFILTERNET:一种基于深度滤波的全频带音频低复杂度语音增强框架 奥地利匈牙利九日游旅程 论文阅读之Study of the General Kalman Filter for Echo Cancellation 小奥看房之鸿荣源珈誉府 杭州往返旅途及西溪喜来登和万怡的体验报告 2022年的第一篇碎碎念
奥地利匈牙利九日游旅程论文阅读之Study of the General Kalman Filter for Echo CancellationDEEPFILTERNET:一种基于深度滤波的全频带音频低复杂度语音增强框架
某些字幕组的某些人,注意! 小奥看房之鸿荣源珈誉府 计算机组成原理笔记第五章(5.1~5.4) 卡尔曼滤波器学习笔记:初步认识 助教:热带海岸线生态系统【昆士兰大学】[2016-01-28] Java语言程序设计(进阶)(第四章)整理
标签聚合
leetcode 学习 算法 生活 python学习 高中 Java Python linux 鸟哥的linux私房菜
最近评论
davidcheung 发布于 6 个月前(02月09日) The problem has been fixed. May I ask if you can s...
tk88 发布于 6 个月前(02月07日) Hmm is anyone else having problems with the pictur...
cuicui 发布于 10 个月前(10月20日) :wink:
niming 发布于 11 个月前(09月19日) 同级校友,能刷到太巧了
davidcheung 发布于 2 年前(08月16日) 我得找一下我之前整理的word文档看一下,如果找到了我就更新一下这篇文章。
Nolan 发布于 2 年前(07月25日) 您的笔记非常有帮助。贴图不显示了,可以更新一下吗?
davidcheung 发布于 3 年前(06月19日) 到没有看webrtc的代码。现在主要在看我们公司的代码了。。。只是偶尔看一看webrtc的东西。。。
aobai 发布于 3 年前(03月13日) gain_change_hangover_ 应该是每三个block 只能够调整一次,这样保证每帧...
匿名 发布于 5 年前(12月30日) 烫
小奥 发布于 5 年前(12月12日) webRTC里面的NS本身我记得就是在C++里面呀

COPYRIGHT © 2025 小奥的学习笔记. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

陕ICP备19003234号-1

鲁公网安备37120202000100号