【模板】最小费用最大流

LinkLink
先把网络流AA了吧

DescriptionDescription

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

InputInput

第一行包含四个正整数N、M、S、T,分别表示点的个数、有向边的个数、源点序号、汇点序号。

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

OutputOutput

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

SampleSample InputInput

4 5 4 3
4 2 30 2
4 3 20 3
2 3 20 1
2 1 30 9
1 3 40 5

SampleSample OutputOutput

50 280

HintHint

对于30%的数据:N<=10,M<=10

对于70%的数据:N<=1000,M<=1000

对于100%的数据:N<=5000,M<=50000

样例说明:

如图,最优方案如下:

第一条流为4–>3,流量为20,费用为3*20=60。

第二条流为4–>2–>3,流量为20,费用为(2+1)*20=60。

第三条流为4–>2–>1–>3,流量为10,费用为(2+9+5)*10=160。

故最大流量为50,在此状况下最小费用为60+60+160=280。

故输出50 280。

TrainTrain ofof ThoughtThought

我们需要找到所有可增广的最小费用路(SPFA)
然后进行增广,然后统计最小费用

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;

int h[10250], F[10250], last[10250], bol[10250];
int x, y, t, n, m, k, cost, start, end;
int Ans_cost, Ans_flow;

struct edge
{
	int x, w, k, cost, h, other;
}wh[500250];
//x为从哪里来//w为到哪里去//k为先可以留多少水//cost为每一单位水流过此处应付的钱//h为下一条边//other为对应的正/反向边
void hw(int x, int y, int k, int cost)
{
	wh[++t] = (edge){x, y, k, cost, h[x], t + 1}, h[x] = t;
	wh[++t] = (edge){y, x, 0, -cost, h[y], t - 1}, h[y] = t;
	//因为一开始没有流水,所以反向边的可流量为0
	//因为反向边是倒流,所以原来的钱应该还回来,所以是负数
}

int SPFA()
{
	memset(F, 0x7f,sizeof(F));
	memset(bol, 0, sizeof(bol));
	F[start] = 0;
	queue<int>s;
	bol[start] = 1;
	s.push(start);
	while(s.size())
	{
		int x = s.front();s.pop();
		for(int i = h[x]; i; i = wh[i].h) 
			if(wh[i].k && F[wh[i].w] > F[x] + wh[i].cost) 
			{
				F[wh[i].w] = F[x] + wh[i].cost;
				last[wh[i].w] = i;//记录经过的路劲
				if(!bol[wh[i].w])bol[wh[i].w] = 1, s.push(wh[i].w);
			}
		bol[x] = 0;
	}
	if(F[end] < F[5200])return 1;
	else return 0;
}

void init()
{
	int l = end, maxx = 1e9;
	while(last[l])
	{
		maxx = min(maxx, wh[last[l]].k);//沿路找最小流量
		l = wh[last[l]].x;
	}
	Ans_cost += F[end] * maxx, Ans_flow += maxx, l = end;//计算最小费用
	while(last[l])
	{
		wh[last[l]].k -= maxx;
		wh[wh[last[l]].other].k += maxx;//增广
		l = wh[last[l]].x;
	}
}
int main()
{
	scanf("%d%d", &n, &m);
	scanf("%d%d", &start, &end);
	for(int i = 1; i <= m; ++i)
	{
		scanf("%d%d%d%d", &x, &y, &k, &cost);
		hw(x, y, k, cost);//建边
	}
	while(SPFA())init();//用SPFA寻找增广路
	printf("%d %d", Ans_flow, Ans_cost);
	return 0;
}
发布了227 篇原创文章 · 获赞 40 · 访问量 1万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 精致技术 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览