[BZOJ 1070] [SCOI2007] 修车 【费用流】


题目链接:BZOJ - 1070

题目分析

首先想到拆点,把每个技术人员拆成 n 个点,从某个技术人员拆出的第 i 个点,向某辆车连边,表示这是这个技术人员修的倒数第 i 辆车。那么这一次修车对整个答案的贡献就是,i * Time[j][k]。 j 是车的编号,k 是技术人员编号。因为这辆车以及之后这个人要修的车的等待时间都增加了 Time[j][k], 所以包括这辆车在内一共有 i 辆车的等待时间加上了这次修车的时间。这样倒着考虑就可以用边的费用很简便的表示修车使所有人增加的时间了。从 S 到所有技术人员拆出的所有点连边,容量 1 , 费用 0 ;从每辆车向 T 连边,容量 1 ,费用 0 。

最后跑一边最小费用最大流就是所有车等待时间的和。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>

using namespace std;

const int MaxN = 1000 + 5, MaxM = 100000 + 5, INF = 999999999;

int n, m, S, T, Ans;
int Time[60 + 5][9 + 5], d[MaxN];

bool Used[MaxN];

queue<int> Q;

inline int gmin(int a, int b) {return a < b ? a : b;}
inline int gmax(int a, int b) {return a > b ? a : b;} 

struct Edge 
{
	int u, v, w, Cost;
	Edge *Next, *Other;
} E[MaxM], *P = E, *Point[MaxN], *Pre[MaxN]; 

inline void AddEdge(int x, int y, int z, int Ct) {
	Edge *Q = ++P; ++P;
	P -> u = x; P -> v = y; P -> w = z; P -> Cost = Ct; 
	P -> Next = Point[x]; Point[x] = P; P -> Other = Q;
	Q -> u = y; Q -> v = x; Q -> w = 0; Q -> Cost = -Ct;
	Q -> Next = Point[y]; Point[y] = Q; Q -> Other = P; 
}

bool Found() {
	memset(d, 0x7f, sizeof(d));
	memset(Used, 0, sizeof(Used));
	d[S] = 0; Used[S] = true; Q.push(S);
	while (!Q.empty()) {
		int x = Q.front();
		Used[x] = false;
		Q.pop();
		for (Edge *j = Point[x]; j; j = j -> Next) {
			if (j -> w && d[j -> v] > d[x] + j -> Cost) {
				d[j -> v] = d[x] + j -> Cost;
				Pre[j -> v] = j;
				if (!Used[j -> v]) {
					Used[j -> v] = true;
					Q.push(j -> v);
				}
			}
		}
	}
	if (d[T] < INF) return true;
	return false;
}

void Augment() {
	int Flow;
	Flow = INF;
	for (Edge *j = Pre[T]; j; j = Pre[j -> u]) Flow = gmin(Flow, j -> w);
	for (Edge *j = Pre[T]; j; j = Pre[j -> u]) {
		j -> w -= Flow;
		j -> Other -> w += Flow;
	}
	Ans += Flow * d[T];
}

int main() 
{
	scanf("%d%d", &m, &n);
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			scanf("%d", &Time[i][j]);
		}
	}
	S = n * m + n + 1; T = S + 1;
	for (int i = 1; i <= n * m; ++i) AddEdge(S, i, 1, 0);
	for (int i = n * m + 1; i <= n * m + n; ++i) AddEdge(i, T, 1, 0);
	for (int i = 1; i <= m; ++i) {
		for (int j = 1; j <= n; ++j) {
			for (int k = 1; k <= n; ++k) {
				AddEdge((i - 1) * n + j, n * m + k, 1, j * Time[k][i]);
			}
		}
	}
	Ans = 0;
	while (Found()) Augment();
	printf("%.2lf\n", (double)Ans / (double)n);
	return 0; 
}

  

优质内容筛选与推荐>>
1、error LNK2005: "void * __cdecl operator new(unsigned int)" (??2@YAPAXI@Z) already defined in LIBCMT.lib(new.obj)链接错误!
2、QT QDockWidget锚接部件 和 QTreeWidget 树形部件 构成树形选择项
3、远程桌面控制工具 -- TeamViewer
4、ASP.NET AJAX控件之AutoComplete
5、第一次运用Eclipse运行代码


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号