HDU-3549 Flow Problem(最大流)

 

Flow Problem

Time Limit: 5000/5000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
 

Problem Description

Network flow is a well-known difficult problem for ACMers. Given a graph, your task is to find out the maximum flow for the weighted directed graph.

 

 

Input

The first line of input contains an integer T, denoting the number of test cases.
For each test case, the first line contains two integers N and M, denoting the number of vertexes and edges in the graph. (2 <= N <= 15, 0 <= M <= 1000)
Next M lines, each line contains three integers X, Y and C, there is an edge from X to Y and the capacity of it is C. (1 <= X, Y <= N, 1 <= C <= 1000)

 

 

Output

For each test cases, you should output the maximum flow from source 1 to sink N.

 

 

Sample Input

 

2 3 2 1 2 1 2 3 1 3 3 1 2 1 2 3 1 1 3 1

 

 

Sample Output

 

Case 1: 1 Case 2: 2

 

 

Author

HyperHexagon

 

 

Source

HyperHexagon's Summer Gift (Original tasks)

 

 

Recommend

 

 

 

       md按照挑战程序设计竞赛写的还保内存,超时,超时代码求大佬指教

    Ford-Fulkerson算法

单向双向都可以

#include <cstdio>
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
#define MAXN 20
#define INF 0x3f3f3f3f
struct edge    //边的三个属性
{
    int  to;
    int cap;   //cap是该弧的最大传输量。是弧尾指向弧头的这段有向距离
    int rev;
};
vector <edge> G[MAXN]; //G[i][j]表示第i个结点的第j位连接对象,感觉不用顺序输入什么的
bool flag[MAXN];
//向图中增加一条从s到t容量为cap的边
void add_edge(int from,int to,int cap)    
{

    edge x1,x2;
    x1.to=to;
    x1.cap=cap;
    x1.rev=G[to].size();
    x2.to=from;
    x2.cap=0;
    x2.rev=G[from].size()-1;
    G[from].push_back(x1); //push_back的功能三个元素的话要用大括号包起来。
    G[to].push_back (x2); //rev存的是逆向对象的地址,巧妙。
} //rev的举例, 1->2 1->3 1->4,那么4的rev就是2 2是1的第二个nextarc 啊,反正就是这个意思,就是3这个对应点了。
//感觉就是就近回溯(近的比较对象是结点的数值。
//通过DFS寻找增广路
int dfs(int v,int t,int f)
{
    int i;
    if(v==t)      //找到这个点的话,直接return f
    return f;     //f代表每一边的实际传输量。 v是起始点,t是终点。
                  //深搜的逆向性决定了f是从最后一条有向弧的cap返回的。
    flag[v]=true;    //最大流的话,每个点只会用一次,极限运用嘛,所以每次就直接让标志为1.
    for(i=0;i<(int)G[v].size();i++)  //
    {

        if(!flag[G[v][i].to]&& G[v][i].cap>0)   //如果这个点没有被找过,(当然是e.to这个点,也就是弧头)d
        {
            int d=dfs( G[v][i].to,t,min(f, G[v][i].cap)); //min(f,e.cap),这步操作是因为一条弧上最多传输cap的流量,这也就是为什么f一开始为什么取无穷大
             //f取无穷大的时候就意味着可以取cap的最大值
             //返回的d也是min(f,G[v][i].cap),f的参数接的就是这个min啊,废话。。。。
            if(d>0)
            {                                         //cap减是等价于f的增加
                G[v][i].cap-=d;                       //这里没有保存f(e)的值,取而代之的是直接改变c(e)的值。
                G[G[v][i].to][G[v][i].rev].cap+=d;    //这一步就是f>0的边构造对应的反向边rev,反向边加处理的,
                return d;                             //cap>0才有路啊,这里也就是构造的增广路。                                                  //同步构建这个残余网络
            }
        }
    }
    return 0;
}
int max_flow(int s,int t)  //
{
    int flow=0;
    while(1)
    {
        memset(flag,0,sizeof(flag));
        int f=dfs(s,t,INF);
        if(f==0)    //return 0的时候就说明已经找不到s->t的路径了。
        return flow;
        flow+=f;
    }
}
int main()
{
    int t;
    int cas=1;
    int i;
    int n,m;
    int from,to,cap;
    scanf("%d",&t);
    while(t--)
    {
      for(i=0;i<=MAXN;i++)
      {
          G[i].clear();
      }
      scanf("%d%d",&n,&m);
      for(i=1;i<=m;i++)
      {
          scanf("%d%d%d",&from,&to,&cap);
          add_edge(from,to,cap);
      }
       printf("Case %d: %d\n",cas++,max_flow(1,n));
    }
    return 0;
}

 在杭电的讨论里膜了一番

Edmonds_Karp算法(感觉跟上面比就是深搜变广搜了)

#include <stdio.h>
#include <algorithm>
#include <queue>
#include <cstring>
#define INF 1000
using namespace std;
int cap[20][20];
int flow[20][20];
int a[1000],p[100];
int n,m,f;
//总的来说就是一次一条最近路,然后剩余容量反向边,图flow总为0
void Edmonds_Karp(int s,int t) //s到t的最大流
{
	queue<int> q;              //这个函数每次用的时候都可以让队列重新定义一次,nice
	memset(flow,0,sizeof(flow));
	f=0;                       //f存的是最大流
	while(1)                    //枚举所有s到t的路线
	{
		memset(a,0,sizeof(a));    //flow是不会清0的
		a[s]=INF;
		q.push(s);
		while(!q.empty())        //队列的广搜BFS形式
		{
			int u=q.front();
			q.pop();
			for(int v=1;v<=n;v++)   //这一次的for起点都是定的
			{                        //只找起点直达没到过的点。
				if(a[v]==0&&cap[u][v]>flow[u][v]) //cap是边的容量矩阵,flow是边的流量矩阵
				{                                 //flow的值没清零,
					p[v]=u;                             //记录反向弧
					q.push(v);
					a[v]=min(a[u],cap[u][v]-flow[u][v]); //a[i]存的就是到达i点的边的f值,这也是为什么一开始a[s]=INF的原因
				}                                        //一次全面广搜只找最靠近一次且是最小的,用最小的去建立剩余容量和反向边。
			}
		}
		if(a[t]==0)//无法继续更新最大流量,则当前流已经是最大流
		{          //两种情况:1.没有可输入的。2.没有可输出的,也就是找不到增广路
            break;
		}
		for(int u=t;u!=s;u=p[u])//从汇点往回走
		{
			flow[p[u]][u]+=a[t];//更新剩余容量,等价于cap值减小
			flow[u][p[u]]-=a[t];//更新反向流 ,等价于这条边的cap加,原理上是cap加,用flow减等效替换了
		}                       //保证cap不变,很巧妙
		f+=a[t];                //作图理解时cap有值,flow为0即可
	}                           //加减的都是末尾的那段容量
}
int main()
{
	int t,i,k=1;

	scanf("%d",&t);
	while(t--)
	{
		scanf("%d %d",&n,&m);
		memset(cap,0,sizeof(cap));
		for(i=0;i<m;i++)
		{
			int x,y,c;
			scanf("%d %d %d",&x,&y,&c);     //这是在告诉我有重边吗??,有点坑啊。
			cap[x][y]+=c;                   //果然图还是邻接矩阵存边方便易懂些。
		}
		Edmonds_Karp(1,n);
		printf("Case %d: %d\n",k++,f);
	}
	return 0;
}


 

dinic算法(邻接矩阵)

#include<iostream>
#include<cstring>
#include<stdio.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 20
struct Edge
{
    int cap;
    int flow;
}edge[MAXN][MAXN];
int level[MAXN];
int n,m;
int dinic_bfs()
{
    int v;
    memset(level,0,sizeof(level));
    queue <int> q;
    q.push(1);      //1 表示起点,n表示终点
    level[1]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(v=1;v<=n;v++)
        {
            if(!level[v]&&edge[u][v].cap>edge[u][v].flow)
            {
                level[v]=level[u]+1;//构建层次网络
                q.push(v);
            }
        }
    }
    return level[n]!=0; //当汇点不再层次网络中,则算法结束。
}
int dinic_dfs(int u,int cap)
{
    int t,v;
    int temp=cap;  //因为是深搜,所以用临时变量代替a数组,来表示到当前点的流量,源点的a是无穷大的,所以初始的cap值为无穷大
    if(u==n)  return cap; //cap传输的值表示当前到u结点的流量。
    for(v=1;v<=n;v++)     //n指的是汇点
    {
        if(edge[u][v].cap>edge[u][v].flow&&level[u]+1==level[v])  
        {
           	    t=dinic_dfs(v,min(temp,edge[u][v].cap-edge[u][v].flow));
				edge[u][v].flow+=t;
				edge[v][u].flow-=t;
				temp-=t;             //相比Edmonds_Karp算法每一次只找一条增广路径,
                                     //dinic算法每一次深搜是枚举完所有到汇点的增广路,
									 //所以cap会减少不只一次,相比较Edmonds_Karp算法的一次而言。
        }
    }
    return cap-temp;
}
int dinic()
{
    int sum=0;
    while(dinic_bfs())            // 不断刷新层次网络
    {
        sum+=dinic_dfs(1,INF);
    }
    return sum;
}
int main()
{
    int test,q,u,v,cap;
    scanf("%d",&test);
    for(q=1;q<=test;q++)
    {
        scanf("%d%d",&n,&m);
        memset(edge,0,sizeof(edge));
        while(m--)
        {
            scanf("%d%d%d",&u,&v,&cap);
            edge[u][v].cap+=cap;
        }
        printf("Case %d: %d\n",q,dinic());
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值