P3980 [NOI2008](经典问题)志愿者招募 最小费用流

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

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

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

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

sample output
14
分析:
首先我们要需要一个超级源点s和超级汇点t;
第i天向第i+1天连上一条容量为INF-a【i】,费用为0的边;
这样就保证了每一天都是前一天推出的,保证了合法性
t向第1天连上一条容量为INF,费用为0的边;
第n+1天向t连上一条容量为INF,费用为0的边;
超级汇点与超级源点是不需要志愿者的,所以设为INF
用当前边跑一遍最小费用流的板子,得到的答案是0,而且最大流第一次跑完里面的最短路是INF-max{a【i】};第二次是max{a【i】};
两次的最小花费都是0
这样没有毛病对吧;

然后你再去有cost值的边
第i类志愿者的服务天数l,r,服务费用w[i]
需要加边为从第l天到第r+1天连一条容量为INF,费用为w[i]的边,这样就能保证在上面运行完第二次最短路的板子之后,得到的cost肯定是min{w[i]},每次都这样最后没有增广路为止
以上是我的解释了,洛谷里面也有其他的解释

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<string>
#include<cmath>
#include<cstring>
#include<set>
#include<queue>
#include<stack>
#include<map>
#define rep(i,a,b) for(int i=a;i<=b;i++)
typedef long long ll;
using namespace std;
const int N=1e5+10;
const ll INF=0x3f3f3f3f3f3f3f3f;
struct node{
    ll from,to,cap,flow,cost;
};
int n,m,s,t;
ll day;
vector<node>edge;
vector<int >G[N];
ll a[N],d[N],p[N];
bool inq[N];
void add(ll from,ll to,ll cap,ll cost){
    edge.push_back((node){from,to,cap,0,cost});
    edge.push_back((node){to,from,0 ,0,-cost});
    int cnt=edge.size();
    G[from].push_back(cnt-2);
    G[to].push_back(cnt-1);
}
//第一次选择天数中最大的天数,然后在选择的过程中通过最短路的板子就能得到cost最少的,然后这两个做乘积就是当前最大流的最小花费
//第二次的时候就从当前的最大流中,通过最短路的板子选择最小的cost,这样又是一个最小的花费
//……直到不能增广为止
bool Bellman(ll &flow,ll &cost){
    for(int i=0;i<=3000;i++) d[i]=INF;
    memset(inq,0,sizeof inq);
    d[s]=0;inq[s]=1;p[s]=0;a[s]=INF;
    queue<int>q;
    q.push(s);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        inq[u]=0;
        for(int i=0;i<G[u].size();i++){
            node e=edge[G[u][i]];
        if(e.cap>e.flow && d[e.to]>d[u]+e.cost){
            d[e.to]=d[u]+e.cost;
            p[e.to]=G[u][i];
            a[e.to]=min(a[u],e.cap-e.flow);
            if(!inq[e.to]){
                q.push(e.to);
                inq[e.to]=1;
            }
          }
        }
    }
    if(d[t] == INF) return false;
//    cout<<d[t]<<" "<<a[t]<<endl;
    flow += a[t];
    cost+=d[t] * a[t];
    int u=t;
    while(u !=s){
        edge[p[u]].flow +=a[t];
        edge[p[u]^1].flow -=a[t];
        u=edge[p[u]].from;
    }
    return true;
}
ll mincost(){
   ll flow=0;ll cost=0;
   int cnt=0;
    while(Bellman(flow,cost));
//    cout<<flow<<endl;
        return cost;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif // ONLINE_JUDGE
    scanf("%d%d",&n,&m);
    s=n+2;t=s+1;
    for(int i=1;i<=n;i++){ll val;
    scanf("%lld",&val);
    add(i,i+1,INF-val,0);
    }
    add(s,1,INF,0);
    add(n+1,t,INF,0);
    for(int i=1;i<=m;i++){ll u,v,w;
        scanf("%lld%lld%lld",&u,&v,&w);
        add(u,v+1,INF,w);
    }
    printf("%lld\n",mincost());
    return 0;
}


发布了229 篇原创文章 · 获赞 17 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/c___c18/article/details/98473777