[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;
}

  

时间: 2024-10-14 18:55:08

[BZOJ 1070] [SCOI2007] 修车 【费用流】的相关文章

bzoj 1070: [SCOI2007]修车 -- 费用流

1070: [SCOI2007]修车 Time Limit: 1 Sec  Memory Limit: 128 MB Description 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的.现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最小. 说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间. Input 第一行有两个m,n,表示技术人员数与顾客数. 接下来n行,每行m个整数.第

BZOJ 1070: [SCOI2007]修车(最小费用最大流)

建图很神奇..建完图其实就是裸的费用流了.. -------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<vector> #include<queue> #define rep(i,n) for(int i

BZOJ 1070 拆点 费用流

1070: [SCOI2007]修车 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 5860  Solved: 2487[Submit][Status][Discuss] Description 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同 的车进行维修所用的时间是不同的.现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最 小. 说明:顾客的等待时间是指从他把车送至维修中心到维

BZOJ SCOI 2007 修车 费用流

题目大意:有一些车和一些修车的人,给出每个人修每个车的时间,问所有人等待的最短平均时间是多少. 思路:记得POJ有一个和这个很像的题,做法是一样的.对于每个人修车的时候,我们只考虑他修车的时间对在它之后修车的人的时间的影响,因此我们只要考虑每一辆车是倒数第几个修的就可以了,然后朴素的建图,跑朴素的费用流,就可以过. CODE: #include <queue> #include <cstdio> #include <cstring> #include <ioman

[BZOJ1070] [SCOI2007] 修车 (费用流 &amp; 动态加边)

Description 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的.现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最小. 说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间. Input 第一行有两个m,n,表示技术人员数与顾客数. 接下来n行,每行m个整数.第i+1行第j个数表示第j位技术人员维修第i辆车需要用的时间T. Output 最小平均等待时间,答案精确到小数点后2位

BZOJ 1070 [SCOI2007]修车 最小费用流

题意:链接 方法:最小费用最大流 解析:前几天做的费用流,很经典,很神奇,很6的一道题,不过当时做完没时间写博客,所以今天给他传上来. 所以没看题解前我并没有做出来这道题,当时满脑子退?流?的思想啥都搞不出来. 看完题解后感觉好神奇! 假设n个人m辆车,那么每一个人都有一行状态,对应的是什么呢?是他倒数第几个修的是哪辆车,而这又是不确定的,所以我们还得将这个点连向m辆车,流量就是1,而费用呢就是倒数第几个乘以修理时间,原因呢?就是如果这个人倒数第几个修的这辆车的话,那么其他人都需要等他修完,所以

[BZOJ 1070][SCOI2007]修车(费用流)

Description 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的.现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最小. 说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间. Solution 非常经典的建图方式,将每一位技术人员拆成n个点分别他是表示倒数第几个修车的,倒数第k个修车的技术人员对所有在等待的人的贡献是tim[i][j]*k #include<iostream>

bzoj 1070: [SCOI2007]修车

1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #define M 10000 5 #define inf 2139062143 6 using namespace std; 7 int cnt=1,n,m,T,d[M],q[2*M],f[M],head[M],next[10*M],u[10*M],v[10*M],w[10*M],fro[10*M],fr[M]; 8 int mp[100][10

P2053 [SCOI2007]修车 费用流

https://www.luogu.org/problemnew/show/P2053 题意 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的.现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最小. 说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间. 思路 左边放n个点,表示n辆车.右边放m * n个点,表示m个工人,拆除n个点表示不同阶段.即如果第i辆车连上了第m个工人的第k个阶段