码迷,mamicode.com
首页 > 其他好文 > 详细

最小费用最大流基础模板(洛谷3381)

时间:2017-05-27 21:49:23      阅读:204      评论:0      收藏:0      [点我收藏+]

标签:ext   head   empty   namespace   接下来   main   rom   数组   序号   

如题,给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用。

输入格式:

第一行包含四个正整数N、M、S、T,分别表示点的个数、有向P3381 【模板】最小费用最大流边的个数、源点序号、汇点序号。

接下来M行每行包含四个正整数ui、vi、wi、fi,表示第i条有向边从ui出发,到达vi,边权为wi(即该边最大流量为wi),单位流量的费用为fi。

输出格式:

一行,包含两个整数,依次为最大流量和在最大流量情况下的最小费用。

输入样例#1:
4 5 4 3
4 2 30 2
4 3 20 3
2 3 20 1
2 1 30 9
1 3 40 5
输出样例#1:
50 280
讲一下具体的思路吧:
首先用前向星储存所有的边,并且将反向边的花费设为相反数(反着流肯定减少),并且此时的流量应为0。
跑一遍spfa(因为有负权边,不能用dij),记得加slf优化,优化的效果十分的明显,将所经过的点和边分别用两个数组记录下来。
然后进入求允许通过的最大流,该边减去最大流,则反向边要加上最大流。
放上啰嗦的代码~
#include<bits/stdc++.h>
#define inf 1e8
#define maxn 12000
#define maxm 120000
using namespace std;

struct Edge
{
  int to,next,w,cost,from;
};
struct Edge edge[maxm];
int head[maxn],dis[maxn];
int n,m,s,t;
int book[maxn],pree[maxn],prep[maxn]; 
int ans_cost=0,ans_flow=0;
int cnt=0;
void add(int u,int v,int w,int cost)
{
   edge[cnt].to=v;
   edge[cnt].from=u; 
   edge[cnt].next=head[u];
   edge[cnt].cost=cost;
   edge[cnt].w=w;
   head[u]=cnt++;
}

int spfa()
{
	  deque<int> q;
	  memset(prep,0,sizeof(prep));
	  memset(pree,0,sizeof(pree));
	  for(int i=1;i<=n;i++)
	    dis[i]=inf;
	  memset(book,0,sizeof(book));
	  q.push_back(s);
	  dis[s]=0;
	  book[s]=1;
	  while(!q.empty())
	  {
	    int j=q.front();q.pop_front();
	    for(int i=head[j];~i;i=edge[i].next)
	    {
		   int v=edge[i].to;
		   if(dis[v]>dis[j]+edge[i].cost&&edge[i].w)
		   {		    
		     dis[v]=dis[j]+edge[i].cost;
			 prep[v]=j;//存前一个点的标号 
			 pree[v]=i;//存前一条边的标号 
			 if(!book[v])//注意book数组的位置orzSA 
			 {
				book[v]=1;		
				if(q.empty()||dis[v]<dis[q.front()])  
			        q.push_front(v);
			    else  q.push_back(v);
		     }
		   }
		}
		book[j]=0;
	  }
	  if(dis[t]<inf)
	    return 1;
	  return 0;
}

void maxflow()
{
    int F=inf;
    for(int i=t;i!=s;i=prep[i])//根据点的情况推算出边的情况orzSA 
       F=min(F,edge[pree[i]].w);
     for(int i=t;i!=s;i=prep[i])
     {
	    int x=pree[i];
		edge[x].w-=F;
	    edge[x^1].w+=F;
	    ans_cost+=edge[x].cost*F;	    
	 }
	 ans_flow+=F;
         
}

int main()
{
    ios::sync_with_stdio(false);
    memset(head,-1,sizeof(head));//边表设为0不能用i,orzSA 
	cin>>n>>m>>s>>t;
	int u,v,w,tt;
	for(int i=1;i<=m;i++)
	{
	  cin>>u>>v>>w>>tt;
	  add(u,v,w,tt);
	  add(v,u,0,-tt);
	}
	while(spfa())
	   maxflow(); 
	cout<<ans_flow<<" "<<ans_cost<<endl;
   return 0;
}

 Tips:数组的大小一定要开组,存边的一定要大于2倍(反向边)。

最后感谢(orz) SA大神的帮助。

最小费用最大流基础模板(洛谷3381)

标签:ext   head   empty   namespace   接下来   main   rom   数组   序号   

原文地址:http://www.cnblogs.com/foreverpiano/p/6914486.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!