题目链接:Drainage Ditches
解题思路:最大流问题,看了一下午的文章和博客。感觉对最大流有了一个初步的了解。给你一张图,里面部分节点间有容量为C的管道,里面的水流不能大于容量限制,给一个指定的源点和汇点,求汇点可以流入水的最大量。
首先要把整个图转化一下才可以更轻松的解决问题,将所有的边转化为一对边,一个称之为正向边,另一个为反向边。例如u, v节点间有一个容量为C的边,就可以抽象成一个C的正向边u -> v,和一个0的反向边 v -> u。正向边表示可以继续增大这个方向的流向最大值为多少,反向变表示可以减少这个方向的流量,也就是退流,最大值为多少。
其次要将整个图分层,用bfs对于没一个节点给予一个深度值,如果汇点没有被访问到那么就是说汇点没有连通。再在这个分层的图中找到一条严格按照深度增加的路径通往汇点,看这条路径上面的流量可否增加。(这里用DFS,也可以自己模拟栈)重复分层,因为之前有些满流量的边被删去了,所以这里新建的图汇点的层数会有变化,这里会找到新的路径,如果路径找不到,说明所有连向汇点的边已经全部满流量了,这是就可以输出了。否则继续此过程。
PS:这里说的可能不是很严谨,但是是我自己的领悟,其他的东西可以看代码。
DINIC算法 复杂度为O(n^2 * m)
#include<stdio.h>
#include<string.h>
#include<queue>
#include<vector>
#define INF 0x5fffffff
#define MAX 210
using namespace std;
typedef struct A{
int s, e, c;
A(){}
A(int a, int b, int d){
s = a, e = b, c = d;
}
}edge;
vector<edge> Map[MAX];
int dist[MAX];
bool vis[MAX];
queue<int> que;
int n, m, s, e, c;
void addedge(int u, int v, int c){//增加边,这里将一条边拆分为两条
Map[u].push_back(edge(u, v, c));//正向边
Map[v].push_back(edge(v, u, 0));//反向边
}
int minNum(int a, int b){
return a < b ? a : b;
}
void bfs(){//这里给图中每一个节点标记深度值dist
int i, j, k;
memset(dist, 0, sizeof(dist));
memset(vis, false, sizeof(vis));
while(!que.empty()) que.pop();
que.push(1);
vis[1] = true;
while(!que.empty()){
int s = que.front();
que.pop();
for(i = 0; i < Map[s].size(); i++){
if(!vis[Map[s][i].e] && Map[s][i].c){//这里必须有容量才能表示这条边相通,可以再流过水流
que.push(Map[s][i].e);
dist[Map[s][i].e] = dist[s] + 1;//深度 + 1
vis[Map[s][i].e] = true;//状态标记
}
}
}
}
int dfs(int u, int ret){//u为当前的节点 ret为当前可以增加的流量
int i, j, ans = 0;
if(u == m){
return ret;
}
for(i = 0; i < Map[u].size(); i++){
//edge tem = Map[u][i];
if(Map[u][i].c && dist[Map[u][i].e] == dist[u] + 1){
int aa = minNum(ret, Map[u][i].c);//一条路线上要保持每条管道内的流量不超过流量限制
int dd = dfs(Map[u][i].e, aa);
Map[u][i].c -= dd;//如果可以增加这里要在正向边减去相应的值
for(j = 0; j < Map[Map[u][i].e].size(); j++){//反向边要加上这个值
if(Map[Map[u][i].e][j].e == u){
Map[Map[u][i].e][j].c += dd;
break;
}
}
ret -= dd;//此点流过最大的流量值
ans += dd;//累计此点变化的流量值
}
}
return ans;
}
int maxflow(){
int ret = 0;
while(true){//每深搜一次都要在标记一次深度值
bfs();
if(!vis[m]){
return ret;
}
ret += dfs(1, INF);
}
}
int main(){
int i, j, k;
//freopen("in.txt", "r", stdin);
while(scanf("%d%d", &n, &m) != EOF){
for(i = 0; i < MAX; i++){
Map[i].clear();
}
for(i = 0; i < n; i++){
scanf("%d%d%d", &s, &e, &c);
addedge(s, e, c);
}
int tem = maxflow();
printf("%d\n", tem);
}
return 0;
}