采用最短增益路径算法
题目链接:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1273
代码注释贴上
#include <stdio.h>
#include <memory.h>
#define MAX 201
#define Inf 1<<20
#define MIN(x,y) ((x) < (y) ? (x) : (y))
int mat[MAX][MAX];
int flow[MAX][MAX];
//-------------------------------------------------------
//总结:利用队列进行广度探索,标记每个点的父结点和可再容纳的最大流量,
//探测到汇点时,汇点的可再容纳流量即是增益的流量,
//反复进行,直到不能探测到汇点为止。
//-------------------------------------------------------
//参数:节点数n,矩阵mat[][],源点source,汇点sink,每条边的流量 flow[][]
//返回:最大流 每条边的流量flow
int ford_fulkerson(int n,int mat[][MAX],int source,int sink,int flow[][MAX])
{
//d[]用来表示还能容纳的容量
int pre[MAX],que[MAX],d[MAX],p,q,t,i,j;
//源点和汇点相同则返回
if (source==sink)
return Inf;
//初始化每条边的流量
for (i=0;i<n;i++)
for (j=0;j<n;j++)
flow[i][j] = 0;
//反复标记,反复查找增益路径
while(1)
{
//pre用来标记父结点,前驱节点,标记为0表示还为找到前驱节点
for (i=0;i<n;i++)
pre[i]=0;
t= source;
pre[source]=source+1;
d[source]=Inf;
//广度探索 que是用数组模拟的队列
for (p=q=0;p<=q && !pre[sink];t=que[p++])
{
for (i=0;i<n;i++)
{
//前向边
if (!pre[i] && mat[t][i]-flow[t][i] > 0){
//i加入队列
que[q++] = i;
//标记第i + 1个顶点的前驱为第t + 1个顶点
pre[i]=t+1;
d[i] = MIN(d[t],mat[t][i] - flow[t][i]);
}
//后向边
else if (!pre[i] && (j=flow[i][t]) > 0){
que[q++]=i;
//第i + 1个顶点的前驱为第t + 1个顶点,因为是后向边,加上-标记,整个成负数
pre[i]=-t-1;
d[i] = MIN(d[t],flow[i][t]);
}
}
}
//结束,不能标记汇点,也就是说不存在该条路径
if (!pre[sink]) break;
//回溯。。。
//从汇点开始增加边的流量,直到源点
for (i=sink;i!=source;)
{
//正向边
if (pre[i] > 0){
flow[pre[i]-1][i] += d[sink];
i=pre[i]-1;
}
//反向边
else{
flow[i][-pre[i]-1] -= d[sink];
i=-pre[i]-1;
}
}
}
//把和源点相接的边的流量相加即最大流量
j = 0;
for (i=0;i<n;i++)
j+=flow[source][i];
return j;
}
int main(){
int N,M;
int i;
int S,E,C;
//FILE *fp = fopen("max_flow.txt","r");
while(scanf("%d%d",&N,&M)/*fscanf(fp,"%d%d",&N,&M)*/ != EOF){
memset(mat,0,sizeof(mat));
for(i = 0;i < N;i++){
scanf("%d%d%d",&S,&E,&C);
//fscanf(fp,"%d%d%d",&S,&E,&C);
mat[S - 1][E - 1] += C;
}
int max_flow = ford_fulkerson(M,mat,0,M - 1,flow);
printf("%d/n",max_flow);
}
return 0;
}