最小费用流

最小费用最大流


引用自:[最小费用最大流算法](http://blog.csdn.net/jarily/article/details/8613208)

### 算法引入: 任何容量网络的最大流流量是唯一且确定的,但是它的最大流f并不是唯一的; 既然最大流f不唯一,因此,如果每条弧上不仅有容量限制,还有费用r; 即每条弧上有一个单位费用的参数,那么在保证最大流的前提下; 还存在一个选择费用最小的最大流问题,即为最小费用最大流问题;
###算法思想: 寻找最大流的方法是从某个可行流出发,找到关于这个流的一条增广路P; 沿着P调整f,对新的可行流又试图寻找关于它的增广路,循环直至不存在增广路为止; 要求最小费用最大流: 如果f是流量为f1的可行流中费用最小者,而p是关于f的所有增广路中费用最小的增广路; 那么沿着p去调整f,得到可行流_f,就是流量为f1的所有可行流中的费用最小者; 这样当f是最大流时,它也就是所要求的最小费用最大流了;

算法内容:

在寻找关于f的最小费用增广路的过程中;
需要构造一个关于f的伴随网络W(f);
把在原网络中寻找关于f的最小费用增广路转换为在伴随网络W(f)中寻找从Vs到Vt的最短路问题;

其中伴随网络W(f)构造为:
顶点为原网络中的顶点;
原网络中的每条弧<u,v>变成两个方向相反的弧<u,v>和<v,u>;
在W(f)中每条弧<u,v>的权值为:

if(f(u,v)<c(u,v)) 
    W(u,v)=r(u,v); 
else if(f(u,v)==c(u,v)) 
    W(u,v)=无穷大(可省略); 
if(f(u,v)>0) 
    W(v,u)=-r(u,v); 
else if(f(u,v)==0) 
    W(v,u)=无穷大(可省略); 

算法流程

算法流程:

  1. 开始取f(0)={0};
  2. 一般若在第k-1步得到的最小费用流为f(k-1),则构造伴随网络W(f(k-1));
  3. 在W(f(k-1))中寻找从Vs到Vt的最短路,若不存在则转⑤,存在转④;
  4. 在原网络G中得到相应的增广路P,在P上对f(k-1)进行调整;调整后新的可行流为f(k),转②;
  5. f(k-1)为最小费用最大流,算法完毕;

###我的模板:


const int INF = 0x3f3f3f3f;
const int N = 211, M = N * N;

struct EG {
    int u, v, cap, cost;
	EG(){}
	EG(int u, int v, int cap, int cost) : u(u), v(v), cap(cap), cost(cost){}
} eg[M];

int tot;
int MaxFlow;
int first[N], Next[M];

void init()
{
	tot = -1;
	memset(first, -1, sizeof(first));
}
void add_edge(int u, int v, int cap, int cost)
{
	eg[++tot] = EG(u, v, cap, cost);
	Next[tot] = first[u]; first[u] = tot;
	eg[++tot] = EG(v, u, 0, -cost);
	Next[tot] = first[v]; first[v] = tot;
}

int dis[N], pre[N];
bool inq[N];
bool spfa(int s, int t)
{
	memset(dis, 0x3f, sizeof(dis));
	memset(inq, false, sizeof(inq));
	queue<int> q;
	dis[s] = 0;
	inq[s] = 1;
	q.push(s);
	while(!q.empty())
	{
		int u = q.front(); q.pop();
		inq[u] = 0;
		for(int i=first[u]; i!=-1; i=Next[i])
		{
			EG &e = eg[i];
			if(e.cap && dis[e.v] > dis[u] + e.cost)
			{
				dis[e.v] = dis[u] + e.cost;
				pre[e.v] = i;
				if(!inq[e.v])
					q.push(e.v), inq[e.v] = 1;
			}
		}
	}
	return dis[t] != INF;
}

int MCMF(int s, int t)
{
	int Cost = 0;
	while(spfa(s,t))
	{
		int mn = INF;
		for(int i=t; i!=s; i=eg[pre[i]].u)
		{
			mn = min(mn, eg[pre[i]].cap);
		}
		for(int i=t; i!=s; i=eg[pre[i]].u)
		{
			eg[pre[i]].cap -= mn;
			eg[pre[i]^1].cap += mn;
		}
		MaxFlow += mn;
		Cost += mn * dis[t];
	}
	return Cost;
}


例题:[hdu-1533](http://acm.hdu.edu.cn/showproblem.php?pid=1533) 题目大意: 给你一个N行M列的矩阵,其中“.”代表空地,“H”代表房子,“m”代表人,其中有n个房子和n个人。现在要求每个人进入一间房子,且人走一步需要支付1美元。 求最小需要花费多少美元才能让所有人都进入到房子中(每个人只能进入一间房子,每个房子只能容纳一个人) 代码: ```c #include #include #include #include #include #include

using namespace std;

const int INF = 0x3f3f3f3f;
const int N = 211, M = N * N;

struct EG {
int u, v, cap, cost;
EG(){}
EG(int u, int v, int cap, int cost) : u(u), v(v), cap(cap), cost(cost){}
} eg[M];

int tot;
int MaxFlow;
int first[N], Next[M];

void init()
{
tot = -1;
memset(first, -1, sizeof(first));
}
void add_edge(int u, int v, int cap, int cost)
{
eg[++tot] = EG(u, v, cap, cost);
Next[tot] = first[u]; first[u] = tot;
eg[++tot] = EG(v, u, 0, -cost);
Next[tot] = first[v]; first[v] = tot;
}

int dis[N], pre[N];
bool inq[N];
bool spfa(int s, int t)
{
memset(dis, 0x3f, sizeof(dis));
memset(inq, false, sizeof(inq));
queue q;
dis[s] = 0;
inq[s] = 1;
q.push(s);
while(!q.empty())
{
int u = q.front(); q.pop();
inq[u] = 0;
for(int i=first[u]; i!=-1; i=Next[i])
{
EG &e = eg[i];
if(e.cap && dis[e.v] > dis[u] + e.cost)
{
dis[e.v] = dis[u] + e.cost;
pre[e.v] = i;
if(!inq[e.v])
q.push(e.v), inq[e.v] = 1;
}
}
}
return dis[t] != INF;
}

int MCMF(int s, int t)
{
int Cost = 0;
while(spfa(s,t))
{
int mn = INF;
for(int i=t; i!=s; i=eg[pre[i]].u)
{
mn = min(mn, eg[pre[i]].cap);
}
for(int i=t; i!=s; i=eg[pre[i]].u)
{
eg[pre[i]].cap -= mn;
eg[pre[i]^1].cap += mn;
}
MaxFlow += mn;
Cost += mn * dis[t];
}
return Cost;
}
char s[N];
pair<int,int> a[N], b[N];
int main()
{
int n, m;
while(scanf("%d %d", &n, &m) == 2 && (n+m))
{
int nx = 0, ny = 0;
for(int i=1; i<=n; i++)
{
scanf("%s", s+1);
for(int j=1; j<=m; j++)
{
if(s[j] == 'H')
a[++nx] = make_pair(i, j);
if(s[j] == 'm')
b[++ny] = make_pair(i, j);
}
}
init();
int s = 0, t = nx + ny + 1;
for(int i=1; i<=nx; i++)
{
add_edge(s, i, 1, 0);
for(int j=1; j<=ny; j++)
{
add_edge(i, nx+j, 1, abs(a[i].first-b[j].first)+abs(a[i].second-b[j].second));
}
}
for(int i=1; i<=ny; i++)
{
add_edge(nx+i, t, 1, 0);
}
printf("%d ", MCMF(s, t));
}
return 0;
}

</br>
[小结练习](http://acm.hust.edu.cn/vjudge/contest/view.action?cid=89465#overview)
</br>
原文地址:https://www.cnblogs.com/WCB-ACM/p/5259553.html