最小费用最大流问题
问题描述:
在最大流的问题上面, 加上了费用: 假设每条边除了有一个流量的限制外 , 还有一个单位流量的费用.
即: 给出最大流量的前提下, 还要附加上最小费用. 这就是最小费用最大流问题.
问题解析:
在此问题下, 你会发想平行边变得有意义了 , 可能会从u到v的弧 , 费用分别会是w1,w2 . 之前我们是
可以将它们合并的. 现在我们无法将它们合并了. 为什么? 因为若e(u,v) , e(v,u)同时存在, 且费用都是负数
代表: 从u流向v的 和 从v流向u的. 这个问题我们可以通过临接表巧妙的解决.
另外我们规定: cost(u,v) + cost(v,u) = 0. 代表反向增广时是减小费用.
实现方法:
spfa算法 + 临接表
spfa算法 + 临接矩阵
代码:
临接表:
bool spfa(int start,int end)
{
int dist[MAX];
queue<int> qu;
p[start] = -1;
qu.push(start);
vis[start] = true;
for(int i = 0; i <= n+2; ++i)
{
dist[i] = (i == start ? 0 : INF);
}
while( !qu.empty() )
{
int k = qu.front();
qu.pop();
vis[k] = false;
for(int e = first[k]; e != -1; e = edges[e].next)
{
if(edges[e].f > 0 && dist[edges[e].v] > dist[k] + edges[e].w)
{
dist[edges[e].v] = dist[k] + edges[e].w;
p[edges[e].v] = e;
if(!vis[edges[e].v])
{
vis[edges[e].v] = true;
qu.push(edges[e].v);
}
}
}
}
if(dist[end] < INF)
return true;
else
return false;
}
int result()
{
int minflow = INF;
for(int i = p[n+1]; i != -1; i = p[edges[i].u])
{
if(minflow > edges[i].f)
minflow = edges[i].f;
}
for(int i = p[n+1]; i != -1; i = p[edges[i].u])
{
edges[i].f -= minflow;
edges[i^1].f += minflow;
mincost += edges[i].w * minflow;
}
}
临接矩阵 :
bool spfa()
{
queue<int> qu;
memset(vis,false,sizeof(vis));
memset(p,-1,sizeof(p));
for(int i = 1; i <= num_man+num_house+1; ++i)
dist[i] = (i == start ? 0 : INF);
vis[start] = true;
qu.push(start);
while( !qu.empty() )
{
int u = qu.front();
qu.pop();
vis[u] = false;
for(int v = 1; v <= num_man+num_house+1; ++v)
{
if(cap[u][v] > flow[u][v] && dist[v] > dist[u] + cost[u][v])
{
dist[v] = dist[u] + cost[u][v];
p[v] = u;
if( !vis[v] )
{
vis[v] = true;
qu.push(v);
}
}
}
}
if(dist[end] >= INF)
return false;
else
return true;
}
void solve()
{
int minflow;
while(true)
{
if(!spfa())
break;
minflow = INF;
for(int u = end; u != start; u = p[u])
{
minflow = min(minflow,cap[p[u]][u] - flow[p[u]][u]);
}
for(int u = end; u != start; u = p[u])
{
flow[p[u]][u] += minflow;
flow[u][p[u]] -= minflow;
}
result += dist[end]*minflow;
}
}