算法训练 网络流裸题

蓝桥杯算法训练 网络流裸题ALGO-247 2020-03-27

我们先看题
试题 算法训练 网络流裸题

资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
  一个有向图,求1到N的最大流
输入格式
  第一行N M,表示点数与边数
  接下来M行每行s t c表示一条从s到t的容量为c的边
输出格式
  一个数最大流量
样例输入
6 10
1 2 4
1 3 8
2 3 4
2 4 4
2 5 1
3 4 2
3 5 2
4 6 7
5 4 6
5 6 3
样例输出
8

数据约定:
n<=1000 m<=10000

最大网络流
从找源点(即题中的1)到汇点(N)的最大流。
一个经典的例子

2
2
2
3
1
1
2
3
4

如果走 1 -> 2 ->3 -> 4 这条路
然后再1->2->4
那么最后的结果是1+1=2

但我们可以看出来这题最优的走法是1 - >2 -> 4 然后 1 -> 3 -> 4
最大流是2+1=3

所以要找一个可以后悔的方法,那就是对每条边加一条反向的边

算法训练 网络流裸题_第1张图片
初始时 每条边的反向的边的容量都是0 当走过一条路之后,这条路正向的容量变为 正向容量 -= 流过的流量 ,而反向流量 += 流过的流量。
一条路正向走一遍,反向再走一遍时,那么这条路上正向流通的量就会被反向流通的量抵消掉一部分
拿上面的例子来说,当第一次走了 1 -> 2 ->3-> 4 时,对2-> 3 后悔的路就会被打开,而 1 -> 2 ->3-> 4 流通的量是1,所以2->3 变为 3-1=2,3->2 变为0+1=1。且1->2 变为2-1=1,3->4 变为1-1=0;(例子中没用的反向的路就不画出来了)

第一次: 1->2 ->3 ->4

2-1=1
2
2
3-1=2
0+1=1
1-1=0
1
2
3
4

第二次: 1->2 ->4

1-1=0
2
2-1=1
2
1
0
1
2
3
4

第三次:1->3->2->4

0
1-1=0
1-1=0
2+1=3
1-1=0
0
1
2
3
4

最后:

0
0
0
3
0
0
1
2
3
4

再从源点1出发的时候 当我们走到 汇点4 流通的量是0 则已经没有可以流通的量了,结束

那么代码怎么写呢?
首先用一个map存放边,需要注意的是题中可能重复对一条路输入,就是说输入s t c的时候可能出现1 2 1 下一次输入又出现1->2这条路 ,所以写的时候不能mp[i][j]=c,而是要mp[i][j]+=c,刚开始提交的时候一直没通过,后来发现了这个问题。
然后定义一个数组 a,a[i]代表从源点到i的最大流通量,即每次源点开始搜索,最后的a[n]就是从源点到汇点的最大流通量,我们还要定义一个数组pre,pre[i]的意义是i的前一个点。
在每一次搜索完成之后,如果a[n]==0,说明已经没有剩余可以流通的量了,直接return;否则,存放结果的res+=本次搜索的流量a[n],res+=a[n],然后从点n开始,到1结束,更新我们这次搜索走过的路的流量,点i的上一个点就是pre[i],所以mp[pre[i]][i]+=a[n],mp[i][pre[i]]-=a[n];

感觉这种题的代码基本就是模板,如果没见过可能这种题很难做,但是会了之后见到解法就冒出来了。

#include 
using namespace std;

int N,M;
int s,t;
int res;

map<int,map<int,int> >  mp;
vector<int> a;
vector<int> pre;

void Solution(){
 	queue<int> que;
	while(1){//从源点开始找

		a.assign(N+1,0);  //初始化 a
		a[s]=INT_MAX;     //源点相当于有无限的流量
		que.push(s);      //每次都是从源点开始找
		
        while(!que.empty()){  //bfs找增广路
        	int v=que.front();
        	que.pop();
			for(map<int,int>::iterator i=mp[v].begin();i!=mp[v].end();++i){
				if(!a[i->first]&&i->second>0){
					pre[i->first]=v;
					a[i->first]=min(a[v],i->second);
					que.push(i->first);
				}
			}
        }
        
		if(a[t]==0)return;//没有剩余的流量了
		
		res+=a[t];
		for(int i=N;i!=1;i=pre[i]){//更新走过的路的流量
			mp[pre[i]][i]-=a[t];
			mp[i][pre[i]]+=a[t];
		}
	}
}
int main(){
	cin>>N>>M;
	for(int i=0;i<M;++i){
		int k,j,l;
		cin>>k>>j>>l;
		mp[k][j]+=l;//是+= 不是= 一条边可能会重复出现
	}
	s=1;//源点
	t=N;//汇点
	pre.assign(N+1,0);
    Solution();
    cout<<res<<endl;
	return 0;
}

你可能感兴趣的:(算法,图论,c++)