第一发网络流纪念下~~~
因为这里e和v都差不多,所以EK和dinic跑出来的时间差不多。纠结的是如何把邻接矩阵换成邻接表的..
EK邻接矩阵写法:
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
typedef __int64 ll;
const int N = 205;
ll a[N][N];
int pre[N];
int vis[N];
queue<int> q;
int n, m;
ll EK()
{
while( !q.empty() )
q.pop();
memset(vis, 0, sizeof( vis ));
memset(pre, 0, sizeof( pre ));
bool ok = 0;
vis[1] = 1;
q.push(1);
while( !q.empty() )//用bfs寻找一条源到汇的可行路径
{
int u = q.front();
q.pop();
for( int i = 1; i <= m; i++ )
{
if( !vis[i] && a[u][i] > 0 )
{
vis[i] = 1;
pre[i] = u;
if( i == m )
{
ok = 1;
break;
}
else
q.push(i);
}
}
if( ok )
break;
}
if( !ok )
return 0;
ll minflow = 99999999;
int now = m;
while( pre[now] )//寻找源到汇路径上容量最小的边,其容量就是此次增加的总流量
{
minflow = min(minflow, a[pre[now]][now]);
now = pre[now];
}
now = m;
while( pre[now] )//沿此路径添加反向边,同时修改路径上每条边的容量
{
a[pre[now]][now] -= minflow;
a[now][pre[now]] += minflow;
now = pre[now];
}
return minflow;
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
memset(a, 0, sizeof( a ));
int u, v;
ll w;
while(n--)
{
scanf("%d%d%I64d", &u, &v, &w);
a[u][v] += w;
}
ll ans = 0, tmp;
while( tmp = EK() )
ans += tmp;
printf("%I64d\n", ans);
}
return 0;
}
EK邻接表写法:
//http://www.xuebuyuan.com/zh-tw/1555201.html
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
typedef __int64 ll;
const int N = 205;
const int inf = 0x3f3f3f3f;
struct node{
int v, nxt;
ll w;
}e[N*2];
int head[N];
queue<int> q;
int vis[N];
int pre[N];
int re[N];//因为使用邻接表之后唯一不确定的就是边的编号记录到达当前点的边的编号,使用时直接edge[re[x]].w就好了,
int n, m;
int cnt;
void init()
{
memset(head, -1, sizeof( head ));
cnt = 0;
}
void add( int u, int v, ll w )
{
e[cnt].w = w;
e[cnt].v = v;
e[cnt].nxt = head[u];
head[u] = cnt++;
e[cnt].w = 0;
e[cnt].v = u;
e[cnt].nxt = head[v];
head[v] = cnt++;
}
ll EK()
{
while( !q.empty() )
q.pop();
memset( vis, 0, sizeof( vis ));
memset( pre, 0, sizeof( pre ));
bool ok = 0;
q.push(1);
vis[1] = 1;
while( !q.empty() )
{
int u = q.front();
q.pop();
for( int i = head[u]; ~i; i = e[i].nxt )
{
int to = e[i].v;
if( !vis[to] && e[i].w > 0 )
{
vis[to] = 1;
pre[to] = u;
re[to] = i;
if( to == m )
{
ok = 1;
break;
}
else
q.push(to);
}
}
if( ok )
break;
}
if( !ok )
return 0;
ll minflow = inf;
int now = m, tmp;
while( pre[now] )
{
minflow = min( minflow, e[re[now]].w);
now = pre[now];
}
now = m;
while( pre[now] )
{
e[re[now]].w -= minflow;
e[re[now]^1].w += minflow;
now = pre[now];
}
return minflow;
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
init();
int u, v;
ll w, ans = 0, tmp;
while(n--)
{
scanf("%d %d %I64d", &u, &v, &w);
add(u, v, w);
}
while( tmp = EK() )
ans += tmp;
printf("%I64d\n", ans);
}
return 0;
}
dinic邻接表写法:
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
typedef __int64 ll;
const int N = 205;
const int inf = 0x3f3f3f3f;
struct node{
int v, nxt;
ll w;
}e[N*2];
int head[N];
int dep[N];
queue<int> q;
int n, m;
int cnt;
void init()
{
memset(head, -1, sizeof( head ));
cnt = 0;
}
void add( int u, int v, ll w )
{
e[cnt].w = w;
e[cnt].v = v;
e[cnt].nxt = head[u];
head[u] = cnt++;
}
//bfs建立层次图
int bfs()
{
memset(dep, 0, sizeof( dep ));
dep[1] = 1;
while( !q.empty() )
q.pop();
q.push(1);
while( !q.empty() )
{
int u = q.front();
q.pop();
if ( u == m )
return 1;
for( int i = head[u]; ~i; i = e[i].nxt )
{
int to = e[i].v;
if( !dep[to] && e[i].w )
{
dep[to] = dep[u] + 1;
q.push(to);
}
}
}
return 0;
}
//当前的节点,minflow 可看成EK里面的增广轨中最小边的权值
ll dinic(int u, ll minflow)
{
if( u == m )
return minflow;
ll tmp = 0; //当前获得的flow
for( int i = head[u]; ~i; i = e[i].nxt )
{
if( e[i].w && dep[e[i].v] == dep[u] + 1 )
{
ll flow = dinic( e[i].v, min( e[i].w, minflow - tmp ));
//flow 为子节点获得的minflow,而传入的参数为“到达子节点边的剩余容量” 与
//“当前边的容量”(因为可能当前的边在这一次增广过程中成为那条满流的边)
//简单说就是利用dfs回溯的性质拿子节点获得的minflow更新上一层,而一层一层递归上次就能更新完整张图
e[i].w -= flow;
e[i^1].w += flow; //逆向边更新
//更新剩余图
tmp += flow;
if( tmp == minflow )
return tmp;
}
}
return tmp;
}
ll solve( )
{
ll tmp = 0;
while( bfs() )
{
tmp += dinic(1, inf);
}
return tmp;
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
init();
int u, v;
ll w, ans = 0;
while(n--)
{
scanf("%d %d %I64d", &u, &v, &w);
add(u, v, w);
add(v, u, 0);//建反向边
}
ans = solve();
printf("%I64d\n", ans);
}
return 0;
}
可参考 http://blog.sina.com.cn/s/blog_ac43e9c3010172l5.html