2013-team4/code/gusfield
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
{{{
给你n个点的容量网络图(n <= 100), 求出两两之间的最大流(最小割)
使用Gusfield算法,时间复杂度O(N*最大流复杂度)
算法描述和解释:
如果(X,Y)是某个s1-t1最小割,(Z,W)是某个s2-t2最小割,那么X∩Z、X∩W、Y∩Z、Y∩W这四项不可能均非空。
也就是说,最小割不可能相互跨立。这个蕴含了,最多一共有N-1个不同的s-t最小割。只需把这些割找出来即可。
寻找方法:在V中任意找两个点a,b,求最大流,把V划分为割X-Y,之后对X、Y分别递归地进行划分。这样就能得到N-1个割了。
}}}
{{{
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int MAXN=200+10;
const int inf=1e9;
struct node {
int v, next;
int flow;
}edge[MAXN*MAXN<<1];
int head[MAXN], que[MAXN];
int level[MAXN];
int n, cnt;
int s, t;
inline void addedge(int u, int v, int a, int b) {
edge[cnt].v=v; edge[cnt].flow=a; edge[cnt].next=head[u]; head[u]=cnt++;
edge[cnt].v=u; edge[cnt].flow=b; edge[cnt].next=head[v]; head[v]=cnt++;
}
bool dinic_bfs() {
memset(level, -1, sizeof(level));
que[0]=s; level[s]=0;
for (int front=0, rear=1; front<rear; front++) {
int u=que[front], v;
for (int now=head[u]; now!=-1; now=edge[now].next)
if (level[v=edge[now].v]==-1&&edge[now].flow>0) {
level[v]=level[u]+1;
que[rear++]=v;
}
}
return (level[t]!=-1);
}
int dinic_dfs(int u, int low) {
if (u==t) return low;
int ret=0, tmp;
for (int v, now=head[u]; now!=-1&&ret<low; now=edge[now].next)
if (level[v=edge[now].v]==level[u]+1&&edge[now].flow>0) {
if (tmp=dinic_dfs(v, min(low-ret, edge[now].flow))) {
ret+=tmp; edge[now].flow-=tmp; edge[now^1].flow+=tmp;
}
}
if (!ret) level[u]=-1;
return ret;
}
int dinic() {
int maxflow=0, tmp;
while (dinic_bfs()) {
while (tmp=dinic_dfs(s, inf)) maxflow+=tmp;
}
return maxflow;
}
int ans[MAXN][MAXN], p[MAXN];
bool vis[MAXN];
void dfs(int u) {
vis[u]=true;
for (int v, now=head[u]; now!=-1; now=edge[now].next)
if (!vis[v=edge[now].v]&&edge[now].flow>0) dfs(v);
}
void gusfield(int n) {
int i, j, cut;
for (i=0; i<n; i++) {
p[i]=0;
for (j=0; j<n; j++) ans[i][j]=inf;
}
for (i=1; i<n; i++) {
for (j=0; j<n; j++) vis[j]=false;
s=i; t=p[i]; cut=dinic();
ans[s][t]=ans[t][s]=cut;
dfs(i);
for (j=i+1; j<n; j++)
if (vis[j]&&p[i]==p[j]) p[j]=i;
for (j=0; j<i; j++)
ans[i][j]=ans[j][i]=min(cut, ans[p[i]][j]);
for (j=0; j<cnt; j+=2) {
edge[j].flow+=edge[j^1].flow;
edge[j^1].flow=0;
}
}
for (i=0; i<n; i++) {
ans[i][i]=0;
for (j=0; j<n; j++)
printf("%d%c", ans[i][j], (j==n-1)?'\n':' ');
}
}
int main() {
int T, x; scanf("%d", &T);
for (int cas=1; cas<=T; cas++) {
scanf("%d", &n);
printf("Case #%d:\n", cas);
if (!n) continue;
cnt=0; memset(head, -1, sizeof(head));
for (int i=0; i<n; i++)
for (int j=0; j<n; j++) {
scanf("%d", &x);
if (x) addedge(i, j, x, 0);
}
gusfield(n);
}
return 0;
}
}}}
给你n个点的容量网络图(n <= 100), 求出两两之间的最大流(最小割)
使用Gusfield算法,时间复杂度O(N*最大流复杂度)
算法描述和解释:
如果(X,Y)是某个s1-t1最小割,(Z,W)是某个s2-t2最小割,那么X∩Z、X∩W、Y∩Z、Y∩W这四项不可能均非空。
也就是说,最小割不可能相互跨立。这个蕴含了,最多一共有N-1个不同的s-t最小割。只需把这些割找出来即可。
寻找方法:在V中任意找两个点a,b,求最大流,把V划分为割X-Y,之后对X、Y分别递归地进行划分。这样就能得到N-1个割了。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int MAXN=200+10;
const int inf=1e9;
struct node {
int v, next;
int flow;
}edge[MAXN*MAXN<<1];
int head[MAXN], que[MAXN];
int level[MAXN];
int n, cnt;
int s, t;
inline void addedge(int u, int v, int a, int b) {
edge[cnt].v=v; edge[cnt].flow=a; edge[cnt].next=head[u]; head[u]=cnt++;
edge[cnt].v=u; edge[cnt].flow=b; edge[cnt].next=head[v]; head[v]=cnt++;
}
bool dinic_bfs() {
memset(level, -1, sizeof(level));
que[0]=s; level[s]=0;
for (int front=0, rear=1; front<rear; front++) {
int u=que[front], v;
for (int now=head[u]; now!=-1; now=edge[now].next)
if (level[v=edge[now].v]==-1&&edge[now].flow>0) {
level[v]=level[u]+1;
que[rear++]=v;
}
}
return (level[t]!=-1);
}
int dinic_dfs(int u, int low) {
if (u==t) return low;
int ret=0, tmp;
for (int v, now=head[u]; now!=-1&&ret<low; now=edge[now].next)
if (level[v=edge[now].v]==level[u]+1&&edge[now].flow>0) {
if (tmp=dinic_dfs(v, min(low-ret, edge[now].flow))) {
ret+=tmp; edge[now].flow-=tmp; edge[now^1].flow+=tmp;
}
}
if (!ret) level[u]=-1;
return ret;
}
int dinic() {
int maxflow=0, tmp;
while (dinic_bfs()) {
while (tmp=dinic_dfs(s, inf)) maxflow+=tmp;
}
return maxflow;
}
int ans[MAXN][MAXN], p[MAXN];
bool vis[MAXN];
void dfs(int u) {
vis[u]=true;
for (int v, now=head[u]; now!=-1; now=edge[now].next)
if (!vis[v=edge[now].v]&&edge[now].flow>0) dfs(v);
}
void gusfield(int n) {
int i, j, cut;
for (i=0; i<n; i++) {
p[i]=0;
for (j=0; j<n; j++) ans[i][j]=inf;
}
for (i=1; i<n; i++) {
for (j=0; j<n; j++) vis[j]=false;
s=i; t=p[i]; cut=dinic();
ans[s][t]=ans[t][s]=cut;
dfs(i);
for (j=i+1; j<n; j++)
if (vis[j]&&p[i]==p[j]) p[j]=i;
for (j=0; j<i; j++)
ans[i][j]=ans[j][i]=min(cut, ans[p[i]][j]);
for (j=0; j<cnt; j+=2) {
edge[j].flow+=edge[j^1].flow;
edge[j^1].flow=0;
}
}
for (i=0; i<n; i++) {
ans[i][i]=0;
for (j=0; j<n; j++)
printf("%d%c", ans[i][j], (j==n-1)?'\n':' ');
}
}
int main() {
int T, x; scanf("%d", &T);
for (int cas=1; cas<=T; cas++) {
scanf("%d", &n);
printf("Case #%d:\n", cas);
if (!n) continue;
cnt=0; memset(head, -1, sizeof(head));
for (int i=0; i<n; i++)
for (int j=0; j<n; j++) {
scanf("%d", &x);
if (x) addedge(i, j, x, 0);
}
gusfield(n);
}
return 0;
}