洛谷3980 BZOJ1061 NOI2008 志愿者招募 费用流

版权声明:本文为博主原创文章,可以转载但是必须声明版权。 https://blog.csdn.net/forever_shi/article/details/89364621

题目链接

题意:
申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要N 天才能完成,其中第i 天至少需要Ai 个人。 布布通过了解得知,一共有M 类志愿者可以招募。其中第i 类可以从第Si 天工作到第Ti 天,招募费用是每人Ci 元。新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这并不是他的特长!于是布布找到了你,希望你帮他设计一种最优的招募方案。
n < = 1000 , m < = 10000 n<=1000,m<=10000

题解:
建二分图的话容易想到但是发现没法限制流量的。

这个题的思路和我博客里发的上一道题的思路差不多,之前那道题是我第一次做只有一排点顺着流下去的网络流题。

我们还是用之前的思路按天建点,每一个点表示一天。我们每天要求的人数各不相同,我们为了保证有流量,把源点流向第一个点的流量设为无穷大。不难想到流量的含义应该是表示这一天要招募的志愿者数,也就是我们要保证每天人数是够的。我们的做法是每天往下一天连一个正无穷减去这一天要的人数的流量,费用为 0 0 的边,表示有这么多要花钱雇佣,剩下的流量只需要流下去保证之后的点都能满流。那么对于一类志愿者,他们工作的时间是 [ s , t ] [s,t] ,所以这要在 s s 花了钱, [ s , t ] [s,t] 内的所有点需要流过的流量都会减少,于是我们就从 s s t + 1 t+1 连边,可以招募无限个,于是流量是正无穷,费用是招募一个人的单价。最后 n + 1 n+1 号点向汇点连流量为正无穷,费用为 0 0 的边。

这要建完图之后跑费用流即可。

感觉用费用流的做法还是非常神啊。

代码:

#include <bits/stdc++.h>
using namespace std;

int n,m,st,ed,hed[11000],cnt,b[11000],ans;
int w[11000],v[11000],inq[11000],f[11000];
queue<int> q;
struct node
{
	int from,to,next,c,cost;
}a[400010];
inline void add(int from,int to,int c,int cost)
{
	a[++cnt].from=from;
	a[cnt].to=to;
	a[cnt].c=c;
	a[cnt].cost=cost;
	a[cnt].next=hed[from];
	hed[from]=cnt;
	a[++cnt].from=to;
	a[cnt].to=from;
	a[cnt].c=0;
	a[cnt].cost=-cost;
	a[cnt].next=hed[to];
	hed[to]=cnt;
}
inline void bfs()
{
	memset(w,0,sizeof(w));
	memset(v,0x3f,sizeof(v));
	w[st]=2e9;
	v[st]=0;
	q.push(st);
	while(!q.empty())
	{
		int x=q.front();
		inq[x]=0;
		q.pop();
		for(int i=hed[x];i;i=a[i].next)
		{
			int y=a[i].to;
			if(a[i].c&&v[y]>v[x]+a[i].cost)
			{
				v[y]=v[x]+a[i].cost;
				w[y]=min(w[x],a[i].c);
				f[y]=i;
				if(!inq[y])
				{
					q.push(y);
					inq[y]=1;
				}
			}
		}
	}
	for(int i=f[ed];i;i=f[a[i].from])
	{
		a[i].c-=w[ed];
		a[i^1].c+=w[ed];
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	st=n+2;
	ed=n+3;
	cnt=1;
	add(st,1,2e9,0);
	for(int i=1;i<=n;++i)
	{
		scanf("%d",&b[i]);
		add(i,i+1,2e9-b[i],0);
	}
	add(n+1,ed,2e9,0);
	for(int i=1;i<=m;++i)
	{
		int x,y,z;
		scanf("%d%d%d",& x,&y,&z);
		add(x,y+1,2e9,z);
	}
	while(1)
	{
		bfs();
		if(w[ed])
		ans+=1ll*w[ed]*v[ed];
		else
		break;
	}
	printf("%lld\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/89364621