网络流 dinic算法 SAP 记录

 

给定一个有向图(若是无向图,那么可以用两条有向边表示一条无向边),每条边都有一个权值表示这条边上的流量,

问题:有n个城市,告诉你m条路以及路上最多分分钟的车流量。

然后我们求   从1 点压入无限多的车,问从n点 分分钟能跑出多少车量的问题称为 网络流(最大流)。

我们称无限多进入车辆的点为:源点

车辆的出口的点为:汇点

 

 

我们可以用如下结构体来记录信息:(这里用的是邻接表建图)

struct Edge
{
	int from, to, cap, flow, next;
}edge[MAXM];

对应信息 分别为起点, 终点, 最大车流量,当前车流量,下一节点的指针。

对于每条边,这条边当前的车流量 = 最大车流量时,称这条边为:满流

对于每条边,cap - flow 就是残余流量(就是还能有多少量车可以从这条路上通过)

 

 

 

为了方便记录,这里定义一个head数组记录下一节点的指针(初始化为-1),用一个变量top来记录总边数。

注意代码中MAXN 为点数,MAXM为边数。

 

下面问题就是怎么记录信息,说白了就是怎么增边。

增边函数:

void addedge(int u, int v, int w)
{
	Edge E1 = {u, v, w, 0, head[u]};//正向建边 
	edge[top] = E1;
	head[u] = top++;
	Edge E2 = {v, u, 0, 0, head[v]};//反向建 
	edge[top] = E2;
	head[v] = top++;
}

 


核心思想:以每条边的最大流量 - 当前流量 为该边的权值,在残余网络(理解为当前网络也行)中查找源点到汇点的最短路径是否存在,若存在继续增广。

 

代码:

int dist[MAXN];//记录该节点到源点的距离
int cur[MAXN];//保存该节点正在考虑的弧,避免重复计算 
int vis[MAXN];//标记该点是否已经存在于当前选择的路径中 
bool BFS(int start, int end)
{
//我们从残余网络找一条从start->end的路径(每条边的边权为最大流量-当前流量) 
//显然当找不到这样的路径时说明不能继续增加流量 则返回false 算法结束
	memset(dist, -1, sizeof(dist));//初始化 
	memset(vis, 0, sizeof(vis));
	queue<int> Q;
	while(!Q.empty()) Q.pop();//清空队列 
	dist[start] = 0;//源点到汇点的距离为0 
	vis[start] = 1;//标记 
	Q.push(start);//进队列 
	while(!Q.empty())
	{
		int u = Q.front();
		Q.pop();
		for(int i = head[u]; i != -1; i = edge[i].next)//遍历 u 所指向的边 
		{
			Edge E = edge[i];
			if(!vis[E.to] && E.cap > E.flow)//当前路径可到达的点未标记 且这条路径没有满流 
			{
				dist[E.to] = dist[u] + 1; //建立层次图
				vis[E.to] = 1;//标记  
				if(E.to == end) return true;//找到路径 
				Q.push(E.to);//进队列 
			}
		}
	} 
	return false;//找不到一条可以从源点到汇点的最短路径 
}
int DFS(int x, int a, int end)//把找到的这个路径上所有的边的当前流量都增加a(a是所找出路径的边中 残余流量的最小值)
{
	if(x == end || a == 0) return a;//优化
	int flow = 0, f;
	for(int& i = cur[x]; i != -1; i = edge[i].next)//从上次考虑的弧开始 
	{
		Edge& E = edge[i];
		if(dist[E.to] == dist[x]+1 && (f = DFS(E.to, min(a, E.cap-E.flow), end)) > 0)//可继续增广 
		{
			E.flow += f;//正向边 
			edge[i^1].flow -= f;//反向边 
			flow += f;//总流量 加上 f 
			a -= f;//最小可增流量 减去f 
			if(a == 0) break; 
		}
	} 
	return flow;
} 
int Maxflow(int start, int end)
{
	int flow = 0;
	while(BFS(start, end))//存在最短路径 
	{
		memcpy(cur, head, sizeof(head));
		flow += DFS(start, INF, end);//增广 
	}
	return flow;
}


 

 

完整代码:源点为1,汇点为n

 

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define MAXN 200+10
#define MAXM 500+10
#define INF 2000000000+10 
using namespace std;
struct Edge
{
	int from, to, cap, flow, next;
}edge[MAXM];
int dist[MAXN], vis[MAXN];//用于求最短路径
int top;//记录边的数目
int head[MAXN];//存储上一节点指针 
int cur[MAXN];
int n, m;//点数 边数 
void init()
{
	top = 0;
	memset(head, -1, sizeof(head));
} 
void addedge(int u, int v, int w)
{
	Edge E1 = {u, v, w, 0, head[u]};//正向建边 
	edge[top] = E1;
	head[u] = top++;
	Edge E2 = {v, u, 0, 0, head[v]};//反向建 
	edge[top] = E2;
	head[v] = top++;
}
void getmap()
{
	int a, b, d;
	while(m--)
	{
		scanf("%d%d%d", &a, &b, &d);
		addedge(a, b, d);//增边 
	}
}
bool BFS(int start, int end)
{
	memset(dist, -1, sizeof(dist));//初始化 
	memset(vis, 0, sizeof(vis));
	queue<int> Q;
	while(!Q.empty()) Q.pop();//清空队列 
	dist[start] = 0;//自己到自己的距离为0 
	vis[start] = 1;//标记 
	Q.push(start);//进队列 
	while(!Q.empty())
	{
		int u = Q.front();
		Q.pop();
		for(int i = head[u]; i != -1; i = edge[i].next)//遍历 u 所指向的边 
		{
			Edge E = edge[i];
			if(!vis[E.to] && E.cap > E.flow)//当前路径可到达的点未标记 且这条路径没有满流 
			{
				dist[E.to] = dist[u] + 1; //建立层次图
				vis[E.to] = 1;//标记  
				if(E.to == end) return true;//找到路径 
				Q.push(E.to);//进队列 
			}
		}
	} 
	return false;//找不到一条可以从源点到汇点的最短路径 
}
int DFS(int x, int a, int end)//把找到的这个路径上所有的边的当前流量都增加a(a是所找出路径的边中 残余流量的最小值)
{
	if(x == end || a == 0) return a;//优化
	int flow = 0, f;
	for(int& i = cur[x]; i != -1; i = edge[i].next)//从上次考虑的弧开始 
	{
		Edge& E = edge[i];
		if(dist[E.to] == dist[x]+1 && (f = DFS(E.to, min(a, E.cap-E.flow), end)) > 0)//可继续增广 
		{
			E.flow += f;//正向边 
			edge[i^1].flow -= f;//反向边 
			flow += f;//总流量 加上 f 
			a -= f;//最小可增流量 减去f 
			if(a == 0) break; 
		}
	} 
	return flow;
} 
int Maxflow(int start, int end)
{
	int flow = 0;
	while(BFS(start, end))//存在最短路径 
	{
		memcpy(cur, head, sizeof(head));
		flow += DFS(start, INF, end);//增广 
	}
	return flow;
}
int main()
{
	while(scanf("%d%d", &n, &m) != EOF)
	{
		init();//初始化 
		getmap();//建图 
		printf("%d\n", Maxflow(1, n));
	}
	return 0;
}


SAP模板:
 

const int MAXN = 100010;//点数的最大值
const int INF = 0x3f3f3f3f;
struct Edge {
    int from, to, cap, next;
};
Edge edge[MAXN * 2];
int head[MAXN], edgenum;
void init() { edgenum = 0; memset(head, -1, sizeof(head)); }
void addEdge(int u, int v, int w) {
    edge[edgenum].from = u;
    edge[edgenum].to = v;
    edge[edgenum].cap = w;
    edge[edgenum].next = head[u];
    head[u] = edgenum++;
}
int dep[MAXN];
int gap[MAXN];//gap[x]=y :说明残留网络中dep[i]==x的个数为y
int n;//n是总的点的个数,包括源点和汇点W
void BFS(int s,int t) {
    memset(dep, -1, sizeof(dep)); memset(gap, 0, sizeof(gap)); gap[0]=1;
    int que[MAXN];
    int Front, rear;
    Front = rear = 0; dep[t]=0;
    que[rear++] = t;
    while(Front != rear) {
        int u = que[Front++];
        if(Front == MAXN) Front = 0;
        for(int i = head[u];i != -1;i = edge[i].next) {
            int v = edge[i].to;
            if(dep[v] != -1)continue;
            que[rear++] = v;
            if(rear == MAXN) rear = 0;
            dep[v] = dep[u] + 1;
            ++gap[dep[v]];
        }
    }
}
int cur[MAXN];
int S[MAXN];
int SAP(int s,int t) {
    int res = 0; BFS(s, t);
    int top = 0;
    memcpy(cur, head, sizeof(head));
    int u = s; int i;
    while(dep[s] < n) {
        if(u == t) {
            int temp = INF;
            int inser;
            for(i = 0; i < top; i++) {
                if(temp > edge[S[i]].cap) {
                   temp = edge[S[i]].cap;
                   inser = i;
               }
            }
            for(i = 0; i < top; i++) {
                edge[S[i]].cap -= temp;
                edge[S[i]^1].cap += temp;
            }
            res += temp;
            top = inser;
            u = edge[S[top]].from;
        }
        if(u != t && gap[dep[u]-1] == 0)//出现断层,无增广路
            break;
        for(i = cur[u];i != -1; i = edge[i].next) {
            if(edge[i].cap != 0 && dep[u] == dep[edge[i].to] + 1)
                break;
        }
        if(i != -1) {
            cur[u] = i;
            S[top++] = i;
            u = edge[i].to;
        }
        else {
            int Min = n;
            for(i = head[u]; i != -1; i = edge[i].next) {
                if(edge[i].cap == 0) continue;
                if(Min > dep[edge[i].to]) {
                    Min = dep[edge[i].to];
                    cur[u] = i;
                }
            }
            --gap[dep[u]];
            dep[u] = Min+1;
            ++gap[dep[u]];
            if(u != s) u = edge[S[--top]].from;
        }
    }
    return res;
}

 



 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值