算法导论学习笔记(20)——最大流

一,概念

1)流网络:简单有向图,且有两个特别的顶点(源点s,汇点t)

2)流的边标识为f(u,v)/c(u,v),流量/容量

3)流的三个性质:1>容量限制 对于所有边 流量<容量

2>反对称性 f(u,v)=-f(v,u)

3>流守恒性 正向流与反响流之和为零

4)割:流网络G=(V,E)的割(S,T)将顶点V划分为S和T=V-S两部分,定义割的容量为C(S)割这条线上S中顶点到T中顶点的容量之和

5)残留网络: 残留容量 c f (u, v) = c(u, v) - f (u, v) //边的容量减去边的实际流量

6)增广路径:对于残留网络 G f 中的一条 s-t 路径 p 称其为增广路径

二,最大流和最小割问题

1)最大流:对于一个流网络 G  (V , E ) ,其流量 f 的最大值称为最大流,最大流问题就
是求一个流网络的最大流。

2)最小割:是指流网络中容量最小的割

三、最大流算法的应用

1)最大流模型

例题:现在有 N 只奶牛,F 种食物和 D 种饮料,每只奶牛喜欢其中的一些食物和饮料。现在每种食物和饮料只能分给一只奶牛,每只奶牛也只能吃一种食物和一种饮料,问最多能使多少奶牛既吃到食物又喝到饮料。

解决:由于有 N 只奶牛、F 种食物和 D 种饮料,因此我们可以将这些东西抽象成图中的点。为了方便,我们将食物放在左边,奶牛放在中间,饮料放在右边。由于食物和饮料的使用限制,我们从源点向每种食物连一条边,从每种饮料向汇点连一条边,容量都为 1。而每只奶牛都有喜欢的食物和饮料,因此将每只奶牛喜欢的食物连到这只奶牛,从这只奶牛连到每种它喜欢的饮料。但这样是否就对了呢?实际还是有问题的,因为经过每只奶牛的食物可能超过一种,这就意味着每只奶牛可能会吃超过一组的食物和饮料,而这在题目中是不允许的。怎么解决这个问题呢?我们又回到了流的基本性质:容量限制f (u, v) - c(u, v) 。因此我们将每只奶牛拆成两个点,同一只奶牛的两个点之间连边,容量为 1。这样们就能保证通过每只奶牛的流量为 1 了。每个流对应每种方案,最大流即为最佳方案。

    求解网络流的基本思想就是Ford-Fulkerson方法(2F方法):

1. 每次寻找增广路径(就是源点到汇点的一条可行路)。

2. flow += 增广路径能流过的流量

3. 更新剩余网络

4. 然后再做增广路,直到做不出增广路;

Ford-Fulkerson方法是一种迭代的方法。开始时,对所有的u,v∈V有f(u,v)=0,即初始状态时流的值为0。在每次迭代中,可通过寻找一条“增广路径”来增加流值。增广路径可以看成是从源点s到汇点t之间的一条路径,沿该路径可以压入更多的流,从而增加流的值。反复进行这一过程,直至增广路径都被找出来,根据最大流最小割定理,当不包含增广路径时,f是G中的一个最大流。在算法导论中给出的Ford-Fulkerson的伪代码如下:

FORD-FULKERSON(G, s, t)
1 for each edge (u, v) ∈ E[G]
2 do f[u, v] ← 0
3 f[v, u] ← 0
4 while there exists a path p from s to t in the residual network Gf
5 do cf(p) ← min {cf(u, v) : (u, v) is in p}
6 for each edge (u, v) in p
7 do f[u, v] ← f[u, v] + cf(p)
8 f[v, u] ← -f[u, v]

整个流程如图所示:



上面代码中关键的一点就是第四行寻找一条增广路径,如果选择的方法不好,就有可能每次增加的流非常少,而算法运行时间非常长,甚至无法终止。对增广路径的寻找方法的不同形成了求最大流的不同算法,这也是Ford-Fulkerson被称之为“方法”而不是“算法”的原因。而Edmonds_Karp算法使用广度优先搜索来找到一条增广路径,不过这里并不严格要求遍历所有顶点,遇到了终点即可终止遍历。

c++代码:

图:



完整头文件和源码:

//Fulkerson.h header file
//Standard Ford-Fulkerson Algorithm
//Author: Fengdong

#include <stack>
#include <list>

using namespace std;

typedef pair<int,int> flowPair;


typedef struct _node
{
    int number;
    int C;
    int F;
    struct _node* next;
    _node(int _number,int _C)
    {
        number = _number;
        C = _C;
        F = 0;
        next = NULL;
    }
}Node;


typedef struct _headNode
{
    Node* next;

    _headNode()
    {
        next = NULL;
    }
}headNode;

class FordFulkerson
{
public:
    FordFulkerson();
    ~FordFulkerson();
    void getMaxFlow();
    void displayInfo();
private:
    void mark();
    void init();
    void constructAdjList(); //construct adjacent table
    void pop(int number);   // pop stack
    void adjustFlow();        // adjust flow if target node marked

    Node* findNode(Node* _current); //which node should be pushed into the stack
    Node* getAdjustedNode(int number,int _target); //change F value when adjust flow
   
   
private:
    static const int headCount = 7;
    flowPair currentState[headCount];
    int Qs;             // how many flow increased
    int augmentingPath ; // how many "augmenting path"
    headNode nodes[headCount];
    stack<Node*>* nodeStack;
    int current;
    int source ;
    int target;

};
/**/


//Fulkerson.cpp source file
//Standard Ford-Fulkerson Algorithm
//Author: Fengdong

#include "stdafx.h"
#include "Fulkerson.h"

FordFulkerson::FordFulkerson()
{
    init();
}

FordFulkerson::~FordFulkerson()
{
    Node* pre;
    Node* suc;
    for( int i=0;i<headCount;i++)
    {
        pre = nodes[i].next;
        while(pre)
        {
            suc = pre->next;
            delete pre;
            pre = suc ;
        }
    }
    delete nodeStack;
}

void FordFulkerson::init()
{
    nodeStack = new stack<Node*>();
    constructAdjList();
    for( int i=0; i<headCount; i++)
    {
        currentState[i] = flowPair(i,-1);
    }
    Qs = 0;
    augmentingPath = 0;
    current = -1;
    source = 0;
    target = 6;
    nodeStack->push(nodes[0].next);
    currentState[0] = flowPair(0,999999);
}

void FordFulkerson::constructAdjList()
{
    Node* tmp ;
    nodes[0].next = new Node(0,0);
    tmp = nodes[0].next ;
    tmp->next = new Node(1,13);
    tmp = tmp->next ;
    tmp->next = new Node(2,9);
   
    nodes[1].next = new Node(3,6);
    tmp = nodes[1].next;
    tmp->next = new Node(4,5);

    nodes[2].next = new Node(1,4);
    tmp = nodes[2].next;
    tmp->next = new Node(3,5);
    tmp = tmp->next ;
    tmp->next = new Node(5,5);

    nodes[3].next = new Node(4,6);
    tmp = nodes[3].next;
    tmp->next = new Node(5,4);
    tmp = tmp->next;
    tmp->next = new Node(6,4);

    nodes[4].next = new Node(6,9);

    nodes[5].next = new Node(6,10);
}

void FordFulkerson::displayInfo()
{
    printf("找到增广路径:%d 个\n",augmentingPath);
    printf("共调整流量:%d\n",Qs);

    printf("最大流量:");

    int sum = 0;
    for(Node* node = nodes[0].next; node != NULL;node = node->next)
        sum += node->F ;

    printf("%d\n",sum);
    printf("\n\n Flow after Adjusted \n\n");

    for (int i=0;i<headCount;i++)
    {
        while(nodes[i].next)
        {
            printf("v%d->v%d : ",i,nodes[i].next->number);
            printf("C= %d F= %d\n",nodes[i].next->C,nodes[i].next->F);
            nodes[i].next = nodes[i].next->next;
        }
        printf("\n");
    }

}

void FordFulkerson::getMaxFlow()
{
    mark();
}

void FordFulkerson::pop(int number)
{
    nodeStack->pop();
    currentState[number].second = -1;
}

Node* FordFulkerson::findNode(Node* _current)
{
    Node* node = nodes[_current->number].next; //head node
    Node* result;

    if (current != -1)
    {
        while ((current != target) && (node->number != current))
        {
            node = node->next;
        }
        node = node->next;
    }
   

    while(node)
    {
        if (node->C != node->F)
        {
            result = node;
            break;
        }
        node = node->next;
    }
   
  // Target node is the high priority child. :)
    while(node)
    {
        if ((node->number == target) && (node->C != node->F))
        {
            result = node;
            break;
        }
        node = node->next;
    }
    return result;
}

Node* FordFulkerson::getAdjustedNode(int number,int _target)
{
    Node* node = nodes[number].next;
    while(node)
    {
        if (node->number == _target)
            break;
        node = node->next;
    }
    return node;
}

//Adjust Flow if I get a augmenting path
void FordFulkerson::adjustFlow()
{
    int Q ;
    int index;
    int fullFlow = -1;
    Node* tmp;

    Q = currentState[target].second ;
    Qs += Q;
    augmentingPath += 1;
   
    index = target;
    while ( index > 0)
    {
        tmp = getAdjustedNode(currentState[index].first,index);
        tmp->F += Q ;       
        if (index != target)
        {           
            currentState[index].second = currentState[index].second - currentState[target].second ;
        }
       
        if (tmp->F == tmp->C)
        {
            fullFlow = index ;
        }
        index = currentState[index].first;
    }

   // a forefather is a full node, so there is no flow to his/her children
    if (fullFlow != -1)
    {
        while (nodeStack->top()->number != fullFlow)
        {
            pop(nodeStack->top()->number);
        }
    }
    return ;
}

void FordFulkerson::mark()
{
    Node* adjNode;
    int minQ = 999999;
   

    while (!nodeStack->empty())
    {   
        if (nodeStack->top()->number == target)
        {
            adjustFlow(); //adjust flow if target node marked
            current = nodeStack->top()->number;
            pop(current);
            continue;
        }

        //there is a full node, pop stack
        if ((nodeStack->top()->C == nodeStack->top()->F ) && (nodeStack->top()->number != 0))
        {
            current = nodeStack->top()->number;
            pop(current);           
            continue;
        }

        adjNode = findNode(nodeStack->top());

        if ( adjNode == NULL)
        {
            current = nodeStack->top()->number;
            pop(current);
            continue;
        }
        else
        {
            if (nodeStack->top()->number != 0)
            {
                minQ = currentState[nodeStack->top()->number].second;
            }
            else
                minQ = 999999;
           //Mark node
            currentState[adjNode->number].first = nodeStack->top()->number;
            currentState[adjNode->number].second = adjNode->C - adjNode->F < minQ ? adjNode->C - adjNode->F :minQ;
            nodeStack->push(adjNode);
            current = -1;

        }
    }//while
}

int main()
{
    FordFulkerson* ffk = new FordFulkerson();
    ffk->getMaxFlow();
    ffk->displayInfo();

    delete ffk;
    return 0;
}



运行结果:

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我知道了,下面是我的回答: 聚类算法是一种无监督学习算法,它可以将数据集中的数据按照某种规则进行分组,使得同一组内的数据相似度较高,不同组之间的数据相似度较低。聚类算法可以帮助我们对数据进行分类、分析和理解,同时也可以为其他机器学习任务提供数据预处理和特征提取的支持。 聚类算法的基本程包括:确定聚类算法的目标函数、选择相似度度量方法、选择聚类算法、确定聚类的数量、进行聚类操作以及评估聚类效果。常见的聚类算法包括K-Means算法、层次聚类算法、密度聚类算法等。 K-Means算法是一种基于距离的聚类算法,它的基本思想是将数据集中的数据划分为K个簇,使得同一簇内的数据相似度较高,不同簇之间的数据相似度较低。K-Means算法的优点是计算复杂度较低,容易实现,但是需要预先指定簇的数量和初始聚类中心。 层次聚类算法是一种基于相似度的聚类算法,它的基本思想是不断合并数据集中相似度最高的数据,直到所有数据都被合并为一个簇或达到预先设定的簇的数量。层次聚类算法的优点是不需要预先指定簇的数量和初始聚类中心,但是计算复杂度较高。 密度聚类算法是一种基于密度的聚类算法,它的基本思想是将数据集中的数据划分为若干个密度相连的簇,不同簇之间的密度差距较大。密度聚类算法的优点是可以发现任意形状的簇,但是对于不同密度的簇分割效果不佳。 以上是聚类算法的基础知识,希望能对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值