[NOI2008]志愿者招募

http://www.lydsy.com/JudgeOnline/problem.php?id=1061

有n天,每天都需要一定数量的人。有m种志愿者,第i个志愿者可以在si到ti天工作,但需要费用ci,求最小费用。

有上下界无源汇费用流。

开一个超级S,超级T,然后每个点计算一下入度与出度之差,f[i]=in[i]-out[i] 

如果>0 向S连一条流量f[i],费用为0的边。

如果<0 向T连一条流量-f[i],费用为0的边。

这道题中,对每一个志愿者,从si向ti+1的点连费用为ci,流量INF的边。

然后每一个点向前一个点连流量INF,费用为0的边。

然后跑一遍费用流~

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define S 0
#define T 1002
#define MAXN 1005
#define MAXL 10000
#define INF 2000000000
using namespace std;
inline int read()
{
    int  x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
int n,m,cnt=1,ans=0;
int head[MAXN+5];
int in[MAXN+5];
int out[MAXN+5];
int d[MAXN+5];
bool mark[MAXN+5];
queue<int> q;

struct edge{
    int to,next,w,c;
}e[MAXL*5];

void ins(int f,int t,int w,int c)
{
    e[++cnt].next=head[f];
    head[f]=cnt;
    e[cnt].to=t;
    e[cnt].w=w;
    e[cnt].c=c;
}

int dfs(int x,int f)
{
    int used=0;mark[x]=1;
    if(x==T) return f;
    for(int i=head[x];i;i=e[i].next)
    {
        int v=e[i].to;
        if(!mark[v]&&d[v]==d[x]-e[i].c&&e[i].w)
        {
            int w=dfs(v,min(f-used,e[i].w));
            ans+=w*e[i].c;
            e[i].w-=w;
            e[i^1].w+=w;
            used+=w;
            if(used==f) return f;
        }
    }
    return used;
}

bool bfs()
{
    q.push(T);
    for(int i=0;i<T;i++) d[i]=INF;
    memset(mark,0,sizeof(mark));
    d[T]=0;mark[T]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(d[u]+e[i^1].c<d[v]&&e[i^1].w)
            {
                d[v]=d[u]+e[i^1].c;
                if(!mark[v])
                {
                    mark[v]=1;q.push(v);
                }
            }
        }
        mark[u]=0;
    }
    return d[S]!=INF;
}

inline void insw(int f,int t,int w,int c)
{
    ins(f,t,w,c);ins(t,f,0,-c);
}

int main()
{
    n=read();m=read();int pre=0;
    for(int i=1;i<=n;i++)
    {
        int x=read();
        if(x>pre) insw(S,i,x-pre,0);
        if(x<pre) insw(i,T,pre-x,0);
        pre=x;
        insw(i+1,i,INF,0);
    }
    insw(n+1,T,pre,0);
    for(int i=1;i<=m;++i)
    {
        int x=read(),y=read(),c=read();
        insw(x,y+1,INF,c);
    }
    while(bfs())
    {
        mark[T]=1;
        while(mark[T])
        {
            memset(mark,0,sizeof(mark));
            dfs(S,INF);
        }
    }
    cout<<ans;
    return 0;
}
FallDream代表秋之国向您问好! 欢迎您来我的博客www.cnblogs.com/FallDream
原文地址:https://www.cnblogs.com/FallDream/p/noi2008T3.html