题目链接: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; }