码迷,mamicode.com
首页 > 其他好文 > 详细

文献阅读01-1-空间众包-最大化任务分配数量-代码实现

时间:2020-02-21 12:58:05      阅读:82      评论:0      收藏:0      [点我收藏+]

标签:头文件   最小值   his   flow   fst   根据   初始   开始   mem   

代码介绍:

       文件数据格式:点的数量,边的数量,源点序号,汇点序号

                每条边:起点,终点,容量

从文件中读取数据,构建网络流图,使用dinic算法求解最大流,采用数组邻接表存储数据。

 

求最大流过程:不断找一条源点到汇点的路径,若有,找出增广路径上每一段权值的最小值,然后构建残余网络。再在残余网络上寻找新的路径,再依据路径的最小值,构建新的残余网络,再寻找新的路径...直至某个残余网络找不到从源点到汇点的路径为止,最大流即为每次最小值之和。

 

Dinic算法:

       先使用宽度优先搜索,根据源点到每个点的最短距离(边数)不同,对每个点进行分层,只要进行到计算出汇点的层次数,即为分层成功。

       之后利用深度优先搜索从前一层向后一层反复寻找增广路径,寻找的增广路径要求满足所有的点分别属于不同的层,且点应满足deppv=deppv-1+1。DFS过程中,进行到了汇点,则说明找到了一条增广路径,此时对总流量进行增加,并消减增广路径各边的容量,并添加反向边,即进行增广。找到一条增广路径后,回溯继续寻找下一个增广路径。

       DFS结束后,对残余网络再次进行分层,当分层操作无法算出汇点的层次是,即求得最大流。

 

头文件:

#include <queue>

#include <fstream>

#include <iostream>

#include <string>

using namespace std;

const int inf = 0x3f3f3f3f;

const int worker_number = 5000;

const int task_number = 5000;

 

struct Edge

{

    int to;//所指向的点

    int next;//下一条边

    int cap;//该边容量

};

 

class Graph

{

private:

    int src;//源点

    int dst;//汇点

    int edge_num;//边数

    int note_num;//点数

    int num;//边的编号

    int max_flow;//最大流量

    int head[worker_number + task_number + 3];//编号1~n单元格来存储1~n号顶点的第一条边的编号

    int cur[worker_number + task_number + 3];//用来复制head数组的值

    int level[worker_number + task_number];//每点的层次

    Edge edges[worker_number + task_number];//存储边

    queue<int> Q;

    void add_edge(int from,int to,int dis);//对网络流图增加边

    bool bfs(int begin, int end);//进行分层

    int dfs(int now,int flow);//寻找增广路径

   

 

public:

    Graph(int n = 0, int m = 0, int s = 0, int d = 0);//初始化,n点的数量,m边的数量,s源点编号,t汇点编号

    void read(string file_name);//从文件中读取数据

    int Dinic();

};

 

Graph::Graph(int n,int m,int s,int d)

{

    this->note_num = n;

    this->edge_num = m;

    this->src = s;

    this->dst = d;

    this->max_flow = 0;

    this->num = -1;

 

    memset(this->head, -1, sizeof(this->head));

    memset(this->cur, -1, sizeof(this->cur));

    memset(this->level, -1, sizeof(this->level));

}

 

void Graph::add_edge(int from,int to,int dis)

{

    this->edges[++this->num].next = this->head[from];

    this->edges[this->num].to = to;

    this->edges[this->num].cap = dis;

    this->head[from] = this->num; 

}

 

void Graph::read(string file_name)

{

    ifstream fin(file_name);

    int x, y, z;

 

    if (fin.good())

    {

         //n结点数,m边数,s源点,d汇点

         fin >> this->note_num >> this->edge_num >> this->src >> this->dst;

         this->num = -1;

 

         for (int i = 0;i < this->edge_num;i++)

         {

             //边:起点,终点,容量

             fin >> x >> y >> z;

 

             this->add_edge(x, y, z);//添加边

             this->add_edge(y, x, 0);//添加反向边

         }

    }

    else

    {

         cout << "文件打开失败!" << endl;

         exit(0);

    }

 

    fin.close();

}

 

//利用bfs进行分层

bool Graph::bfs(int begin, int end)

{

    memset(this->level, -1, sizeof(this->level));//更新层次,对残余网络进行分层

    int now;//当前点

 

    while (!this->Q.empty())this->Q.pop();

    this->level[begin] = 0;//起点深度为0

    this->Q.push(begin);

 

    while (!this->Q.empty())

    {

         now = this->Q.front();

         this->Q.pop();

 

         for (int i = this->head[now];i != -1;i = this->edges[i].next)

         {

             //如果该点没有被标记层次并且该边的流量不为零

             if (this->level[this->edges[i].to] == -1 && this->edges[i].cap)

             {

                 this->level[this->edges[i].to] = this->level[now] + 1;

                 this->Q.push(this->edges[i].to);

             }

         }

    }

   

    return this->level[this->dst] != -1;//若没有到达汇点,则返回false

}

 

//flow为源点到now的路径中的最小边权

int Graph::dfs(int now, int flow)

{

    if (now == this->dst)return flow;//如果当前点为汇点,则返回flow

 

    int detla = flow, d, f;

    for (int i = this->cur[now];i != -1;i = this->edges[i].next)

    {

         //寻找路径时,点要属于不同的层次并且边为通

         if (this->level[this->edges[i].to] == (this->level[now] + 1) && (this->edges[i].cap > 0))

         {

             //向下递归,求后续结点的最大流量,即最小边权

             this->cur[now] = this->edges[i].next;// 即先定义一个cur数组,在dfs之前把邻接表的head数组拷贝到cur里面,然后在遍历now的时候同时把cur里面的边的下标后移,以达到将用完的边略去的目的

             d = (detla < this->edges[i].cap) ? detla : this->edges[i].cap;

             f = this->dfs(this->edges[i].to, d);

 

             this->edges[i].cap -= f;

             //处理反向边,存边时从0开始,边和其反向边相邻

             this->edges[i ^ 1].cap += f;

             detla -= f;//更新本节点的残余量

             //若本节点的残余量消耗完,则返回

             if (detla == 0)break;

         }

    }

 

    return flow - detla;//返回该点已经被允许流过的流量

}

 

int Graph::Dinic()

{

    while (this->bfs(this->src, this->dst))

    {

         for (int i = 1;i <= this->note_num;i++)this->cur[i] = this->head[i];// 把邻接表的head数组拷贝到cur里面

         this->max_flow += this->dfs(this->src, inf);

    }

 

    return this->max_flow;

}

 

 

源文件:

#include "标头.h"

 

int main()

{

    Graph G;

    string file_name;

 

    cout << "请输入存储数据的文件" << endl;

    cin >> file_name;

 

    G.read(file_name);

    cout << "最大流:" << G.Dinic() << endl;

 

    return 0;

}

文献阅读01-1-空间众包-最大化任务分配数量-代码实现

标签:头文件   最小值   his   flow   fst   根据   初始   开始   mem   

原文地址:https://www.cnblogs.com/fangzhiyou/p/12340849.html

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