ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

BZOJ 1061: [Noi2008]志愿者招募 最小费用最大流

2019-08-09 22:53:19  阅读:333  来源: 互联网

标签:ch dist 1061 招募 志愿者 int incf BZOJ Noi2008


title

BZOJ 1061
Description

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

Input

  第一行包含两个整数N, M,表示完成项目的天数和可以招募的志愿者的种类。 接下来的一行中包含N 个非负整数,表示每天至少需要的志愿者人数。 接下来的M 行中每行包含三个整数Si, Ti, Ci,含义如上文所述。为了方便起见,我们可以认为每类志愿者的数量都是无限多的。

Output

  仅包含一个整数,表示你所设计的最优方案的总费用。

Sample Input

3 3
2 3 4
1 2 2
2 3 5
3 3 2

Sample Output

14

HINT

1 ≤ N ≤ 1000,1 ≤ M ≤ 10000,题目中其他所涉及的数据均 不超过2^31-1。

analysis

这道题是要好好写写题解的。

刚开始我所想到的模型便是 有源汇下界网络流,嗯,不会处理,愣了一个小时。

下午放学来机房,请教了 \(Chdy\),发现建图有个小技巧,真妙!

我原本的思路是把第 \(i\) 天与第 \(i+1\) 天之间连边,但是容量肯定要比第 \(i\) 天所需志愿者数量大的,所以有点难受。

然后,这个小技巧就是:
我们观察到:为了方便起见,我们可以认为每类志愿者的数量都是无限多的。
嗯,那么我们就把所有的边容量设为 \(INF\),为了满足每天至少需要的志愿者数 \(x\),就用 \(INF-x\) 当做他们的容量,强行将下界转化为上界,即我们写的最普通的网络流。

还有一个思维技巧:我们将所有边的容量都归属于他左边的端点,这样我们若是要保证第 \(n\) 天的所需志愿者数,就需要建一个 \(n+1\) 点,但是我们同时也要保证和汇点连的那条边的容量是 \(INF\),所以就需要再建一个 \(汇点t\) ,与 \(n+1\) 连容量为 \(INF\),代价为 \(0\)的边。

然后其他的就没什么必要说了,都是套路。

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+10,inf=0x3f3f3f3f;

char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
    x=0;
    T f=1, ch=getchar();
    while (!isdigit(ch) && ch^'-') ch=getchar();
    if (ch=='-') f=-1, ch=getchar();
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}

template<typename T>inline void write(T x)
{
    if (!x) { putchar('0'); return ; }
    if (x<0) putchar('-'), x=-x;
    T num=0, ch[20];
    while (x) ch[++num]=x%10+48, x/=10;
    while (num) putchar(ch[num--]);
}

int ver[maxn<<1],edge[maxn<<1],Next[maxn<<1],cost[maxn<<1],head[maxn],len=1;
inline void add(int x,int y,int z,int c)
{
    ver[++len]=y,edge[len]=z,cost[len]=c,Next[len]=head[x],head[x]=len;
    ver[++len]=x,edge[len]=0,cost[len]=-c,Next[len]=head[y],head[y]=len;
}

int s,t;
int dist[maxn],incf[maxn],pre[maxn];
bool vis[maxn];
inline bool spfa()
{
    memset(dist,0x3f,sizeof(dist));
    memset(vis,0,sizeof(vis));
    queue<int>q;q.push(s);
    dist[s]=0,vis[s]=1,incf[s]=1<<30;
    while (!q.empty())
    {
        int x=q.front();
        q.pop();
        vis[x]=0;
        for (int i=head[x]; i; i=Next[i])
        {
            if (!edge[i]) continue;
            int y=ver[i];
            if (dist[y]>dist[x]+cost[i])
            {
                dist[y]=dist[x]+cost[i];
                incf[y]=min(incf[x],edge[i]);
                pre[y]=i;
                if (!vis[y]) q.push(y),vis[y]=1;
            }
        }
    }
    if (dist[t]==inf) return false;
    else return true;
}

long long maxflow,ans;
inline void update()
{
    int x=t;
    while (x!=s)
    {
        int i=pre[x];
        edge[i]-=incf[t];
        edge[i^1]+=incf[t];
        x=ver[i^1];
    }
    maxflow+=incf[t];
    ans+=dist[t]*incf[t];
}

int main()
{
    int n,m;
    read(n);read(m);
    s=0,t=n+m+1;
    for (int i=1,x; i<=n; ++i) read(x),add(i,i+1,inf-x,0);//将下界网络流转化为上届网络流的一个神奇操作
    add(s,1,inf,0),add(n+1,t,inf,0);
    for (int i=1,si,ti,ci; i<=m; ++i) read(si),read(ti),read(ci),add(si,ti+1,inf,ci);
    while (spfa()) update();
    write(ans);
    return 0;
}

标签:ch,dist,1061,招募,志愿者,int,incf,BZOJ,Noi2008
来源: https://www.cnblogs.com/G-hsm/p/11329960.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有