BZOJ 2245 SDOI 2011 工作安排 费用流

题目大意:有一些商品需要被制造,有一些员工,每一个员工会做一些物品,然而这些员工做物品越多,他们的愤怒值越大,这满足一个分段函数。给出哪些员工可以做哪些东西,给出这些分段函数,求最小的愤怒值以满足需要被制造的商品。

思路:费用流。我写的朴素费用流好像很慢,有时间学一学费用流的多路增广。

由于题目中满足那些分段函数是满足单调递增的性质的,所以就可以如下建图:

S->每个人,费用0,流量INF

每个商品->T,费用0,流量为需要改商品的数量

对于每个人虚拟建n个节点(n<=5)

每个人->虚拟节点,费用为分段函数的值,流量INF

每个人的虚拟节点->那个人能够做出的商品,费用0,流量INF

这样跑EK费用流就可以了。

CODE:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 2010
#define MAXE 600010
#define INF 0x3f3f3f3f
#define S 0
#define T (MAX - 1)
using namespace std;

int persons,staffs;
bool work[300][300];

int head[MAX],total = 1;
int next[MAXE],aim[MAXE],flow[MAXE],cost[MAXE];

int src[MAX];

int f[MAX],p[MAX],from[MAX];
bool v[MAX];

inline void Add(int x,int y,int f,int c);
long long EdmondsKarp();
bool SPFA();

int main()
{
	cin >> persons >> staffs;
	for(int i = 1;i <= persons; ++i) {
		Add(S,i,INF,0);
		Add(i,S,0,0);
	}
	for(int x,i = 1;i <= staffs; ++i) {
		scanf("%d",&x);
		Add(i + persons,T,x,0);
		Add(T,i + persons,0,0);
	}
	for(int i = 1;i <= persons; ++i)
		for(int j = 1;j <= staffs; ++j)
			scanf("%d",&work[i][j]);
	int now = persons + staffs;
	for(int cnt,i = 1;i <= persons; ++i) {
		scanf("%d",&cnt);
		for(int j = 1;j <= cnt; ++j)
			scanf("%d",&src[j]);
		src[cnt + 1] = INF;
		for(int x,j = 1;j <= cnt + 1; ++j) {
			scanf("%d",&x);
			Add(i,++now,src[j] - src[j - 1],x);
			Add(now,i,src[j] - src[j - 1],-x);
			for(int k = 1;k <= staffs; ++k)
				if(work[i][k]) {
					Add(now,persons + k,INF,0);
					Add(persons + k,now,0,0);
				}
		}
	}
	cout << EdmondsKarp() << endl;
	return 0;
}

inline void Add(int x,int y,int f,int c)
{
	next[++total] = head[x];
	aim[total] = y;
	flow[total] = f;
	cost[total] = c;
	head[x] = total;
}

long long EdmondsKarp()
{
	long long re = 0;
	while(SPFA()) {
		int remain = INF;
		for(int i = T;i != S;i = from[i])
			remain = min(remain,flow[p[i]]);
		for(int i = T;i != S;i = from[i]) {
			flow[p[i]] -= remain;
			flow[p[i]^1] += remain;
		}
		re += f[T] * remain;
	}
	return re;
}

bool SPFA()
{
	static queue<int> q;
	while(!q.empty())	q.pop();
	q.push(S);
	memset(f,0x3f,sizeof(f));
	memset(v,false,sizeof(v));
	f[S] = 0;
	while(!q.empty()) {
		int x = q.front(); q.pop();
		v[x] = false;
		for(int i = head[x];i;i = next[i])
			if(flow[i] && f[aim[i]] > f[x] + cost[i]) {
				f[aim[i]] = f[x] + cost[i];
				if(!v[aim[i]]) {
					v[aim[i]] = true;
					q.push(aim[i]);
				}
				from[aim[i]] = x;
				p[aim[i]] = i;
			}
	}
	return f[T] != 0x3f3f3f3f;
}
时间: 2024-10-13 07:14:55

BZOJ 2245 SDOI 2011 工作安排 费用流的相关文章

bzoj 2245: [SDOI2011]工作安排(费用流)

2245: [SDOI2011]工作安排 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 1446  Solved: 692 [Submit][Status][Discuss] Description 你的公司接到了一批订单.订单要求你的公司提供n类产品,产品被编号为1~n,其中第i类产品共需要Ci件.公司共有m名员工,员工被编号为1~m员工能够制造的产品种类有所区别.一件产品必须完整地由一名员工制造,不可以由某名员工制造一部分配件后,再转交给另

【bzoj2245】[SDOI2011]工作安排 费用流

题目描述 你的公司接到了一批订单.订单要求你的公司提供n类产品,产品被编号为1~n,其中第i类产品共需要Ci件.公司共有m名员工,员工被编号为1~m员工能够制造的产品种类有所区别.一件产品必须完整地由一名员工制造,不可以由某名员工制造一部分配件后,再转交给另外一名员工继续进行制造. 我们用一个由0和1组成的m*n的矩阵A来描述每名员工能够制造哪些产品.矩阵的行和列分别被编号为1~m和1~n,Ai,j为1表示员工i能够制造产品j,为0表示员工i不能制造产品j. 如果公司分配了过多工作给一名员工,这

P2488 [SDOI2011]工作安排 费用流

\(\color{#0066ff}{ 题目描述 }\) 你的任务是制定出一个产品的分配方案,使得订单条件被满足,并且所有员工的愤怒值之和最小.由于我们并不想使用Special Judge,也为了使选手有更多的时间研究其他两道题目,你只需要输出最小的愤怒值之和就可以了. \(\color{#0066ff}{输入格式}\) \(\color{#0066ff}{输出格式}\) 仅输出一个整数,表示最小的愤怒值之和. \(\color{#0066ff}{输入样例}\) 2 3 2 2 2 1 1 0 0

BZOJ2245 SDOI2011 工作安排 费用流

题意:有n类产品,其中第i类产品共需要Ci件.有m名员工,员工能够制造的产品种类有所区,一件产品必须完整地由一名员工制造,对于员工i,他的愤怒值与产品数量之间的函数是一个Si+1段的分段函数.当他制造第1~Ti,1件产品时,每件产品会使他的愤怒值增加Wi,1,当他制造第Ti,1+1~Ti,2件产品时,每件产品会使他的愤怒值增加Wi,2……设Ti,0=0,Ti,si+1=+∞,那么当他制造第Ti,j-1+1~Ti,j件产品时,每件产品会使他的愤怒值增加Wi,j, 1≤j≤Si+1.求在订单满足的前

BZOJ 1927 SDOI 2010 星际竞速 费用流

题目大意:宇宙空间中进行了一次竞速大赛.有两种飞行方式,第一种是通过正常的道路,但是只能从标号小的飞到标号大的地方:第二种是直接过去,但是需要花费固定的时间.问正好遍历一次所有的点最少需要的多少时间. 思路:费用流.把每个点拆点,S到每个点的起点连费用0的边,向每个终点连费用为固定费用的边,图中原有的边从一个的起点连到另一个点的终点.然后每个点的终点向T连边.跑最小费用最大流就是最后的答案. CODE: #include <queue> #include <cstdio> #inc

【BZOJ 2245】[SDOI2011]工作安排

Description 你的公司接到了一批订单.订单要求你的公司提供n类产品,产品被编号为1~n,其中第i类产品共需要Ci件.公司共有m名员工,员工被编号为1~m员工能够制造的产品种类有所区别.一件产品必须完整地由一名员工制造,不可以由某名员工制造一部分配件后,再转交给另外一名员工继续进行制造. 我们用一个由0和1组成的m*n的矩阵A来描述每名员工能够制造哪些产品.矩阵的行和列分别被编号为1~m和1~n,Ai,j为1表示员工i能够制造产品j,为0表示员工i不能制造产品j. 如 果公司分配了过多工

BZOJ 3876 支线剧情 | 有下界费用流

BZOJ 3876 支线剧情 | 有下界费用流 题意 这题题面搞得我看了半天没看懂--是这样的,原题中的"剧情"指的是边,"剧情点"指的才是点. 题面翻译过来大概是这样: 有一个DAG,每次从1号点出发,走过一条路径,再瞬移回1号点.问:想要遍历所有的边,至少要走多少路程(瞬移回1号点不算路程). 题解 我们用有上下界费用流的模型,建个图: 原图中的每条边,流量范围是\([1, +\infty]\),表示至少走一次,可以走无限次,这条边的费用就是边权. 原图中的每个

[BZOJ 1221] [HNOI2001] 软件开发 【费用流 || 三分】

题目链接:BZOJ - 1221 题目分析 算法一:最小费用最大流 首先这是一道经典的网络流问题.每天建立两个节点,一个 i 表示使用毛巾,一个 i' 表示这天用过的毛巾. 然后 i 向 T 连 Ai (第 i 天需要的毛巾数).从 S 向 i' 连 Ai ,这样这天新增的用过的毛巾就是 Ai 了. 然后 i' 可以连向 (i+1)' ,表示留到下一天再处理,i' 还可以流向 i+p+1 和 i+q+1,表示洗了之后再次使用,这两种边是有费用的. 还有就是新购买毛巾,从 S 向 i 连,费用就是

BZOJ 1572: [Usaco2009 Open]工作安排Job( 贪心 )

贪心... 按截止时间排序 , 然后从小到大考虑 . 假设当前考虑第 i 个任务 , 若目前已选工作数 < D_i , 那就选 i ; 否则 若已选工作中利润最小的比 P_i 小 , 那就去除它 , 并选择 i . 可以用heap ( priority_queue ) 维护 -------------------------------------------------------------------------- #include<cstdio> #include<algo