zoj 3362 Beer Problem 最小费用流

       别人都说是赤裸裸的最小费用流,自己想了很久,都想不到如何做,最后看了别人解题报告才懂。

       关键还是在构图和转化。源点是1,添加一个汇点,汇点到各个城市的边的容量是各个城市卖啤酒的钱的相反数。通过spfa求cost的最短路径,若最短路径的cost小于0,说明这条路径是赚钱的,否则是亏本的,算法停止。

#include<iostream>

#include <queue>

#include <cstdio>

#include <cstring>

using namespace std;



const int N = 105;

const int M = 2005;

const int INF = 1000000000;



struct edge

{

	int v;

	int cap;

	int cost;

	int next;

};



edge E[M*4+N*2];

int head[N];

int cost[N];

int pre_v[N], pre_e[N];

bool inQ[N];



int n, m;

int cnt;



void add_edge(int u, int v, int cap, int cost)

{

	E[cnt].v = v;

	E[cnt].cap = cap;

	E[cnt].cost = cost;

	E[cnt].next = head[u];

	head[u] = cnt++;



	E[cnt].v = u;

	E[cnt].cap = 0;

	E[cnt].cost = -cost;

	E[cnt].next = head[v];

	head[v] = cnt++;

}



int mcmf(int s, int t, int n)

{

	queue<int> Q;

	int u;

	int mincost = 0;

	while (1)

	{

		memset(inQ, false, sizeof(inQ));

		//memset(pre_e, -1, sizeof(pre_e));

		memset(pre_v, -1, sizeof(pre_v));

		Q.push(s);

		for (int i = 0; i < n; i++)

			cost[i] = INF;

		cost[s] = 0;

		inQ[s] = true;



		//spfa

		//根据每条边的cost求最短路

		while (!Q.empty())

		{

			u = Q.front();

			Q.pop();

			inQ[u] = false;



			for (int i = head[u]; i != -1; i = E[i].next)

			{

				int v = E[i].v;

				if (E[i].cap && cost[u] + E[i].cost < cost[v])

				{

					cost[v] = cost[u] + E[i].cost;

					if (!inQ[v])

					{

						Q.push(v);

						inQ[v] = true;

					}

					

					pre_v[v] = u; pre_e[v] = i;

				}

			}

		}



		//当cost[t]大于0时,说明当前的买卖时亏本的,退出

		if (cost[t] >= 0)

			break;



		int f = INF;

		for (int i = t; i != s; i = pre_v[i])

		{

			int v = pre_e[i];

			f = min(f, E[v].cap);

		}



		mincost += f*cost[t];



		for (int i = t; i != s; i = pre_v[i])

		{

			int u = pre_e[i];

			E[u].cap -= f;

			E[u^1].cap += f;

		}



	}



	return -mincost;

}



int main()

{

	int u, v, cap, w;

	while (cin >> n >> m)

	{

		if (!n && !m)

			break;

		

		cnt = 0;

		memset(head, -1, sizeof(head));

		for (int i = 2; i <= n; i++)

		{

			scanf("%d", &w);

			add_edge(i, n+1, INF, -w);

		}



		for (int i = 0; i < m; i++)

		{

			scanf("%d%d%d%d", &u, &v, &cap, &w);

			add_edge(u, v, cap, w);

			add_edge(v, u, cap, w);

		}



		cout << mcmf(1, n+1, n+2) << endl;

	}

	return 0;

}

你可能感兴趣的:(ZOJ)