题目链接:UVa 10594 - Data Flow
这道题是无向图的最小费用最大流问题,看清楚是无向图的。这么说无向图和有向图的费用流问题有什么区别呢?主要是反向边的问题。首先我们说一下最大流问题中的反向边,我们需要将其cap[u][v]=0表示容量为0,而在费用流问题中添加了费用,所以肯定不能像之前那么简单处理了,那怎么办呢?在有向图中,没有存在的反向边我们用cap[u][v]=0表示容量为0,cost[v][u]=-cost[u][v]表示取反的费用,简单说就是讲这部分费用减除,相当于没有走。 现在可以说一下无向图和有向图的不同了,既然两个方向都是可以走的,那么我们就将原本有的一条边变化出了四条边,两个原有边,两个反向边,原有两个边相互独立,不能将这两个原有边看成互为反向边,否则就出现了环路,spfa就走不通了。
然后说一下最大费用怎么处理,也可以说成要求的费用怎么处理。 加一个点0,表示为起点,设置cap[0][1]=D(题目给的流量D),cost[0][1]=0。显然到终点的最大流量不可能超过D,那么小于D的话表示不可解,等于D表示有解。
要注意一下算法中使用的亦或操作符,有注释。
这题目就是一个模版题,我就不用我写的了。。我在这里找到了一个比较规整的模版,加了一些注释,下面贴出来,如果不理解跟着代码调试一下就懂了。
#include <cstdio> #include <cstring> #include <queue> using namespace std; const int MAX = 110; const long long fd = 1; const long long INF = fd << 60; int n,m; struct edge { int u; int v; long long cost; int flow; int cap; int next; }e[50000]; struct node { int u; int v; int w; }E[5050]; long long dis[MAX]; int first[MAX]; int p[MAX]; bool vis[MAX]; int edgenum; int f,d,cap; long long c; void add(int u,int v,long long w,int num) { e[edgenum].u = u; e[edgenum].v = v; e[edgenum].cap = num; e[edgenum].cost = w; e[edgenum].flow = 0; e[edgenum].next = first[u]; first[u] = edgenum++; e[edgenum].u = v; e[edgenum].v = u; e[edgenum].cap = 0; e[edgenum].cost = -w; e[edgenum].flow = 0; e[edgenum].next = first[v]; first[v] = edgenum++; } void EK() { queue <int> q; c = f = 0; while(1) { for(int i = 0;i <= n; i++) dis[i] = (i == 0 ? 0 : INF); memset(vis,false,sizeof(vis)); memset(p,-1,sizeof(p)); q.push(0); while(!q.empty()) { int u = q.front(); q.pop(); vis[u] = false; for(int k = first[u]; k != -1; k = e[k].next) { int v = e[k].v; if(e[k].cap > e[k].flow && dis[v] > dis[u] + e[k].cost) { dis[v] = dis[u] + e[k].cost; p[v] = k; if(!vis[v]) { vis[v] = true; q.push(v); } } } } if(dis[n] == INF) break; int a = 999999999; for(int u = p[n]; u != -1; u = p[e[u].u])//这里的u是指第u条边,不再是邻接矩阵里面的顶点 a = min(a,e[u].cap - e[u].flow); for(int u = p[n]; u != -1; u = p[e[u].u])//增广,这里的u是指第u条边,不再是邻接矩阵里面的顶点 { e[u].flow += a; e[u^1].flow -= a;//注意看这里的亦或符号 比如u为2,亦或后就是3了,恰好第二条边的反向边就是第三条边 } c += dis[n] * a;//注意需要乘以距离 f += a; } } int main() { int i,j; while(scanf("%d %d",&n,&m)!=EOF) { edgenum = 0; memset(first,-1,sizeof(first)); for(i = 0;i < m; i++) { scanf("%d %d %lld",&E[i].u,&E[i].v,&E[i].w); } scanf("%d %d",&d,&cap); add(0,1,0,d); for(i = 0; i < m; i++) { add(E[i].u,E[i].v,E[i].w,cap); add(E[i].v,E[i].u,E[i].w,cap); } EK(); if(f == d) printf("%lld\n",c); else printf("Impossible.\n"); } return 0; }