【bzoj2245】 SDOI2011—工作安排

http://www.lydsy.com/JudgeOnline/problem.php?id=2245 (题目链接)

题意

  n个产品,每个需要造C[i]件;m个员工,每个员工可以制造一些产品;每个员工的愤怒值是关于制造产品数的递增分段函数。所有员工的愤怒值之和最少是多少。

Solution

  按照题意连边构图,跑费用即可。

细节

  开LL。

代码

// bzoj2245
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
#define inf 2147483640
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;

const int maxn=100010;
struct edge {int from,to,next,w,c;}e[maxn*10];
int p[maxn],f[maxn],d[maxn],head[maxn],vis[maxn],s[maxn],T[300][300],w[300][300];
int n,m,cnt=1,es,et;

void link(int u,int v,int w,int c) {
	e[++cnt]=(edge){u,v,head[u],w,c};head[u]=cnt;
	e[++cnt]=(edge){v,u,head[v],0,-c};head[v]=cnt;
}
LL SPFA() {
	queue<int> q;
	memset(f,0,sizeof(f));
	memset(d,0x7f,sizeof(d));
	q.push(es);d[es]=0;f[es]=inf;
	while (!q.empty()) {
		int x=q.front();q.pop();vis[x]=0;
		for (int i=head[x];i;i=e[i].next) if (e[i].w && d[e[i].to]>d[x]+e[i].c) {
				d[e[i].to]=d[x]+e[i].c;
				f[e[i].to]=min(f[x],e[i].w);
				p[e[i].to]=i;
				if (!vis[e[i].to]) q.push(e[i].to),vis[e[i].to]=1;
			}
	}
	if (f[et]==0) return 0;
	for (int i=p[et];i;i=p[e[i].from]) e[i].w-=f[et],e[i^1].w+=f[et];
	return (LL)f[et]*d[et];
}
LL EK() {
	LL ans=0;
	while (1) {
		LL x=SPFA();
		if (x==0) return ans;
		ans+=x;
	}
}
int main() {
	scanf("%d%d",&m,&n);
	es=10000;et=es+1;
	for (int x,i=1;i<=n;i++) {
		scanf("%d",&x);
		link(i+m,et,x,0);
	}
	for (int i=1;i<=m;i++) link(es,i,inf,0);
	for (int i=1;i<=m;i++)
		for (int x,j=1;j<=n;j++) {
			scanf("%d",&x);
			if (x) link(n+m+i,j+m,inf,0);
		}
	for (int i=1;i<=m;i++) {
		scanf("%d",&s[i]);
		for (int j=1;j<=s[i];j++) scanf("%d",&T[i][j]);
		for (int j=1;j<=s[i]+1;j++) scanf("%d",&w[i][j]);
		for (int j=1;j<=s[i];j++) link(i,n+m+i,T[i][j]-T[i][j-1],w[i][j]);
		link(i,n+m+i,inf,w[i][s[i]+1]);
	}
	printf("%lld",EK());
	return 0;
}

  

原文地址:https://www.cnblogs.com/MashiroSky/p/6201698.html