NOI2008 志愿者招募

题目传送门

Description

一个项目分\(n\)天完成,每天需要\(a_i\)个志愿者。志愿者有\(m\)种招募方式,第\(i\)种从第\(s_i\)天工作到第\(t_i\)天,工资\(c_i\)元。求完成项目的最少花费。\((n\leq 10^3,m\leq 10^4)\)

Solution

数据范围和题目描述都指向网络流,且是最小费用最大流。

但如何建图?这就不显然了。

以每天为一个节点,并建立源点和汇点。

源点连第一天,最后一天连汇点,容量为\(inf\),费用为\(0\)

每一天连后一天,容量为\(inf-a_i\),费用为\(0\)

对于每一类志愿者,\(s_i\)\(t_i\),容量为\(inf\),费用为\(c_i\)

然后跑最小费用最大流即可。

为什么这样建图可行?不难发现,只要题目有解,最大流一定为\(inf\),也就是用有费用的流将所有的\(a_i\)都填满了。

Code

#include<cstdio>
#include<queue>
#include<cstring>
#define int long long
#define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
#define per(i, a, b) for (register int i=(a); i>=(b); --i)
using namespace std;
const int N=10005, M=200005, inf=1ll<<60;
struct node{int to, nxt, flow, cost;}edge[M];
int head[N], vis[N], last[N], pre[N], flow[N], cost[N];
int a[N], s[M], t[M], c[M], n, m, cnt, S, T;
void prep(){memset(head, -1, sizeof(head)); cnt=-1;}

inline int read()
{
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}

void add(int u, int v, int f, int w)
{
    edge[++cnt]=(node){v, head[u], f, w}; head[u]=cnt;
    edge[++cnt]=(node){u, head[v], 0, -w}; head[v]=cnt;
}

bool spfa()
{
    rep(i, 1, n+3) cost[i]=inf; cost[S]=0;
    memset(vis, 0, sizeof(vis)); vis[S]=1;
    rep(i, 1, n+3) flow[i]=inf;
    queue<int> q; q.push(S); pre[T]=-1;
    while (!q.empty())
    {
        int u=q.front(); q.pop();
        for (int i=head[u]; ~i; i=edge[i].nxt)
        {
            vis[u]=0;
            int v=edge[i].to, w=edge[i].cost, f=edge[i].flow;
            if (f>0 && cost[u]+w<cost[v]) 
            {
                pre[v]=u; last[v]=i; cost[v]=cost[u]+w;
                flow[v]=min(flow[u], f);
                if (!vis[v]) vis[v]=1; q.push(v);
            }
        }
    }
    return ~pre[T];
}

int EK()
{
    int maxflow=0, mincost=0;
    while (spfa())
    {
        maxflow+=flow[T]; mincost+=flow[T]*cost[T];
        int u=T;
        while (u^S)
        {
            edge[last[u]].flow-=flow[T];
            edge[last[u]^1].flow+=flow[T];
            u=pre[u];
        }
    }
    return mincost;
}

signed main()
{
    prep(); n=read(); m=read(); S=n+2; T=n+3;
    rep(i, 1, n) a[i]=read();
    rep(i, 1, m) s[i]=read(), t[i]=read(), c[i]=read();
    add(S, 1, inf, 0); add(n+1, T, inf, 0);
    rep(i, 1, n) add(i, i+1, inf-a[i], 0);
    rep(i, 1, m) add(s[i], t[i]+1, inf, c[i]);
    printf("%lld\n", EK());
    return 0;
}

posted @ 2019-04-22 14:30  OIerC  阅读(230)  评论(0编辑  收藏  举报