• [NOI2008]志愿者招募 网络流 建模


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

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

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

    题解:

    最小费用流还是很明显的,关键在于如何建模。
    我们逐天考虑一下:
    首先,雇员的数量是不限的,每天的需求量是固定的。
    这道题难就难在每个人的工作时间不是一天,而是第 $[s_{i},t_{i}]$ 天之间,这就让我们很难建出约束关系。
    我们考虑这么做:
    首先将每一天看作是一个节点,向下一天连一条容量为 $inf-need_{i}$ 的边,费用为 $0$。
    再将每个 $[s_{i},t_{i}]$ 中的 $s_{i}$ 向 $t_{i}+1$ 连一条边,容量无限,费用为每个人所需费用。
    特别地,我们将起点到 1 号点,$n$ 号点向终点连的边容量为 $inf$,这样的话程序在优先跑完费用为 $0$ 的边后就会跑那些需要花费的边来满足题目要求来补足那些流量不够的边。
    总体来说,还是不太好想的。

    Code:

    #include<vector>
    #include<algorithm>
    #include<cstdio>
    #include<string>
    #include<cstring>
    #include<queue>
    using namespace std;
    void setIO(string a){
        freopen((a+".in").c_str(),"r",stdin);
    }
    const int maxn=2000;
    const int INF=1000000+23666;
    typedef long long ll;
    int s,t,n,m;
    struct Edge{
        int from,to,cap,cost;
        Edge(int u,int v,int c,int f):from(u),to(v),cap(c),cost(f){}
    };
    struct MCMF{
        vector<Edge>edges;
        vector<int>G[maxn];
        int d[maxn],inq[maxn],a[maxn],flow2[maxn];
        queue<int>Q;
        long long  ans;
        int flow;
        void addedge(int u,int v,int c,int f){
            edges.push_back(Edge(u,v,c,f));    //正向弧
            edges.push_back(Edge(v,u,0,-f));   //反向弧
            int m=edges.size();
            G[u].push_back(m-2);
            G[v].push_back(m-1);
        }
        int SPFA(){
            for(int i=0;i<maxn-1;++i)d[i]=INF,flow2[i]=INF;
            memset(inq,0,sizeof(inq));int f=INF;
            d[s]=0,inq[s]=1;Q.push(s);
            while(!Q.empty()){
                int u=Q.front();Q.pop();inq[u]=0;
                int sz=G[u].size();
                for(int i=0;i<sz;++i){
                      Edge e=edges[G[u][i]];
                      if(e.cap>0&&d[e.to]>d[u]+e.cost){
                          a[e.to]=G[u][i];
                          d[e.to]=d[u]+e.cost;
                          flow2[e.to]=min(flow2[u],e.cap);
                          if(!inq[e.to]){inq[e.to]=1;Q.push(e.to);}
                      }
                }
            }
            if(d[t]==INF)return 0;
            f=flow2[t];
            flow+=f;
            int u=edges[a[t]].from;
            edges[a[t]].cap-=f;
            edges[a[t]^1].cap+=f;
            while(u!=s){
                edges[a[u]].cap-=f;
                edges[a[u]^1].cap+=f;
                u=edges[a[u]].from;
            }
            ans+=(ll)(d[t]*f);
            return 1;
        }
        int maxflow(){
            while(SPFA());
            return flow;
        }
        ll getcost(){return ans;}
    }T;
    int main(){
        //setIO("input");
        int n,m,a;
        scanf("%d%d",&n,&m);
        s=0,t=n+2;
        T.addedge(s,1,INF,0);
        T.addedge(n+1,t,INF,0);
        for(int i=1;i<=n;++i){
            scanf("%d",&a);
            T.addedge(i,i+1,INF-a,0);
        }
        for(int i=1;i<=m;++i)
        {
            int x,y,c;
            scanf("%d%d%d",&x,&y,&c);
            T.addedge(x,y+1,INF,c);
        }
    
        T.maxflow();
        printf("%lld",T.getcost());
        return 0;
    }
    

      

  • 相关阅读:
    [Java] 编写第一个java程序
    [Java] 环境变量设置
    [ActionScript 3.0] 常用的正则表达式
    [ActionScript 3.0] 正则表达式
    Python学习之==>URL编码解码&if __name__ == '__main__'
    Python学习之==>面向对象编程(一)
    Linux下安装redis-4.0.10
    Linux下编译安装Python-3.6.5
    Python学习之==>发送邮件
    Python学习之==>网络编程
  • 原文地址:https://www.cnblogs.com/guangheli/p/9872993.html
Copyright © 2020-2023  润新知