zoukankan      html  css  js  c++  java
  • 【洛谷P3980】志愿者招募

    题目

    题目链接:https://www.luogu.com.cn/problem/P3980
    申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要 (n) 天才能完成,其中第 (i) 天至少需要 (a_i) 个人。布布通过了解得知,一共有 (m) 类志愿者可以招募。其中第 (i) 类可以从第 (s_i) 天工作到第 (t_i) 天,招募费用是每人 (c_i) 元。新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这并不是他的特长!于是布布找到了你,希望你帮他设计一种最优的招募方案。
    (nleq 1000,mleq 10^4)

    思路

    如果是一般的建图方法,无法处理这种一个点对一段区间的费用流。所以需要改变建图的思路。
    建立 (n+1) 个点,点 (i) 向点 (i+1) 连一条流量为 (+infty-a_i),费用为 (0) 的边,表示限制第 (i) 天至少要有 (a_i) 名志愿者。
    然后对于一类志愿者 (j),从 (s_j)(t_{j}+1) 连一条流量为 (+infty),费用为 (c_j) 的边,如果这条边流过了 (k) 的流量,表示这一类志愿者选 (k) 名。
    然后连边 ((S,1,+infty,0),(n+1,T,+infty,0))
    这样的话,因为有流量的限制,当 (+infty) 的流量从源点流向 (1) 时,至少要有 (a_1) 的流量从志愿者的边流走,因为从 (1)(2) 的边的流量只有 (infty-a_1)。后面的点同理。
    然后跑最小费用最大流即可。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=1010,M=30010;
    const ll Inf=1e18;
    int n,m,S,T,tot=1,head[N],pre[N];
    ll cost,dis[N];
    bool vis[N];
    
    struct edge
    {
    	int next,to;
    	ll flow,cost;
    }e[M];
    
    void add(int from,int to,ll flow,ll cost)
    {
    	e[++tot]=(edge){head[from],to,flow,cost};
    	head[from]=tot;
    	swap(from,to);
    	e[++tot]=(edge){head[from],to,0,-cost};
    	head[from]=tot;
    }
    
    bool spfa()
    {
    	memset(dis,0x3f3f3f3f,sizeof(dis));
    	queue<int> q;
    	q.push(S); dis[S]=0;
    	while (q.size())
    	{
    		int u=q.front(); q.pop();
    		vis[u]=0;
    		for (int i=head[u];~i;i=e[i].next)
    		{
    			int v=e[i].to;
    			if (e[i].flow && dis[v]>dis[u]+e[i].cost)
    			{
    				dis[v]=dis[u]+e[i].cost; pre[v]=i;
    				if (!vis[v]) vis[v]=1,q.push(v);
    			}
    		}
    	}
    	return dis[T]<Inf;
    }
    
    void addflow()
    {
    	ll minf=Inf;
    	for (int i=T;i!=S;i=e[pre[i]^1].to)
    		minf=min(minf,e[pre[i]].flow);
    	for (int i=T;i!=S;i=e[pre[i]^1].to)
    		e[pre[i]].flow-=minf,e[pre[i]^1].flow+=minf;
    	cost+=minf*dis[T];
    }
    
    void MCMF()
    {
    	while (spfa()) addflow();
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	scanf("%d%d",&n,&m);
    	S=N-1; T=N-2;
    	add(S,1,Inf,0); add(n+1,T,Inf,0);
    	for (int i=1,x;i<=n;i++)
    	{
    		scanf("%d",&x);
    		add(i,i+1,Inf-x,0);
    	}
    	for (int i=1,x,y,z;i<=m;i++)
    	{
    		scanf("%d%d%d",&x,&y,&z);
    		add(x,y+1,Inf,z);
    	}
    	MCMF();
    	cout<<cost;
    	return 0;
    }
    
  • 相关阅读:
    二维凸包
    luogu_P1287 盒子与球
    luogu_P1993 小K的农场
    luogu_P1712 [NOI2016]区间
    luogu_P2444 [POI2000]病毒
    luogu_P2154 [SDOI2009]虔诚的墓主人
    20191005-T3-U91353 放射性
    编译原理 笔记2 词法分析
    DFA到等价正则表达式的转化
    软件分析笔记10 Soundiness
  • 原文地址:https://www.cnblogs.com/stoorz/p/15020999.html
Copyright © 2011-2022 走看看