1. 程式人生 > >洛谷 P3980 [NOI2008]志願者招募 費用流

洛谷 P3980 [NOI2008]志願者招募 費用流

題目描述

申奧成功後,布布經過不懈努力,終於成為奧組委下屬公司人力資源部門的主管。布布剛上任就遇到了一個難題:為即將啟動的奧運新專案招募一批短期志願者。經過估算,這個專案需要N 天才能完成,其中第i 天至少需要Ai 個人。 布布通過了解得知,一共有M 類志願者可以招募。其中第i 類可以從第Si 天工作到第Ti 天,招募費用是每人Ci 元。新官上任三把火,為了出色地完成自己的工作,布布希望用盡量少的費用招募足夠的志願者,但這並不是他的特長!於是布布找到了你,希望你幫他設計一種最優的招募方案。

輸入輸出格式

輸入格式:
第一行包含兩個整數N, M,表示完成專案的天數和可以招募的志願者的種類。 接下來的一行中包含N 個非負整數,表示每天至少需要的志願者人數。 接下來的M 行中每行包含三個整數Si, Ti, Ci,含義如上文所述。為了方便起見,我們可以認為每類志願者的數量都是無限多的。

輸出格式:
僅包含一個整數,表示你所設計的最優方案的總費用。

輸入輸出樣例

輸入樣例#1:
3 3
2 3 4
1 2 2
2 3 5
3 3 2
輸出樣例#1:
14
說明

1 ≤ N ≤ 1000,1 ≤ M ≤ 10000,題目中其他所涉及的資料均 不超過2^31-1。

分析:
想到網路流以後,一個直觀的想法就是源點連人,人連可以被僱傭的日子,日子連匯點,然後跑最小費用最大流。可以發現一滴流量(僱傭一個人)只能用其中一天,其實每天都是能用的。

那我們可以把點串聯或直接跨過這個區間,顯然串聯的話不同類的人會相互影響,那就只能直接跨過這個區間了。

我們設每天都要僱傭

k個志願者,對於一類志願者,可以管[l,r],那麼從lr+1連一條流量為k,費用為ci的邊,流一滴流量相當於僱傭一個志願者。第i天到第i+1天連一條邊,流量為kai,費用為0,表示第i天需要ai個志願者。因為假設每天要k個志願者,第i天實際只要ai個志願者,相當於僱傭了kai個免費的只在第i天的志願者。

我們可以把k設為inf,只保證費用最小,但是費用流會為了更大的流而花費更多的錢,但是每一滴從源點到匯點的流都只表示[1,n]都僱傭了一個志願者,所以我們希望的最大流是inf,所以從源點向1點流量為

inf,費用為0的邊,n+1到匯點連流量為inf,費用為0的邊,問題就解決了。

程式碼:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#define LL long long

const int inf=2147483647;
const int maxn=1007;
const int maxe=40007;

using namespace std;

int n,m,x,y,c,s,t,cnt;
LL ans;
int pre[maxn],ls[maxn],dis[maxn],v[maxn];

struct edge{
    int x,y,w,c,op,next;
}g[maxe];

queue <int> q;

void add(int x,int y,int w,int c)
{
    g[++cnt]=(edge){x,y,w,c,cnt+1,ls[x]};
    ls[x]=cnt;
    g[++cnt]=(edge){y,x,0,-c,cnt-1,ls[y]};
    ls[y]=cnt;
}

bool spfa() 
{
    for (int i=s;i<=t;i++)
    {
        dis[i]=inf/2;
        v[i]=0;
    }
    while (!q.empty()) q.pop();
    dis[s]=0;
    v[s]=1;
    q.push(s);
    while (!q.empty())
    {
        int x=q.front();
        q.pop();
        for (int i=ls[x];i>0;i=g[i].next)
        {
            int y=g[i].y;
            if ((g[i].w) && (dis[x]+g[i].c<dis[y]))
            {
                dis[y]=dis[x]+g[i].c;
                pre[y]=i;
                if (!v[y])
                {
                    v[y]=1;
                    q.push(y);
                }
            }
        }
        v[x]=0;
    }
    if (dis[t]==inf/2) return 0;
    return 1;
}

void mcf()
{
    int flow=inf,i=t;
    while (i!=s)
    {
        flow=min(flow,g[pre[i]].w);
        i=g[pre[i]].x;
    }
    i=t;
    while (i!=s)
    {
        g[pre[i]].w-=flow;
        g[g[pre[i]].op].w+=flow;
        ans+=(LL)g[pre[i]].c*1ll*flow;
        i=g[pre[i]].x;
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    s=0; t=n+2;
    add(s,1,inf,0);
    add(n+1,t,inf,0);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        add(i,i+1,inf-x,0);
    }
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&c);
        add(x,y+1,inf,c);
    }   
    while (spfa()) 
      mcf();
    printf("%lld",ans);
}