这道题是无向图的最小费用最大流问题,看清楚是无向图的。这么说无向图和有向图的费用流问题有什么区别呢?主要是反向边的问题。首先我们说一下最大流问题中的反向边,我们需要将其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;
}