1. 程式人生 > >[Noi2008]志願者招募(BZOJ1061)

[Noi2008]志願者招募(BZOJ1061)

相關連結:

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

題目大意:

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

輸入

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

輸出

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

樣例輸入

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

樣例輸出

14

提示

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

個人感覺這是個神題,如果說真的在考場上遇到,估計就只有30~50分的暴力分,但是對於這個題我覺得最難的肯定不是程式碼的實現,最難的還是在題目中抽象出數學模型,


設僱傭第i類志願者的人數為X[i],每個志願者的費用為V[i],第j天僱傭的人數為P[j],則每天的僱傭人數應滿足一個不等式,如上表所述,可以列出

P[1] = X[1] + X[2] >= 4

P[2] = X[1] + X[3] >= 2

P[3] = X[3] + X[4] +X[5] >= 5

P[4] = X[5] >= 3

對於第i個不等式,新增輔助變數Y[i] (Y[i]>=0) ,可以使其變為等式

P[1] = X[1] + X[2] - Y[1] = 4

P[2] = X[1] + X[3] - Y[2] = 2

P[3] = X[3] + X[4] +X[5] - Y[3] = 5

P[4] = X[5] - Y[4] = 3

在上述四個等式上下新增P[0]=0,P[5]=0,每次用下邊的式子減去上邊的式子,得出

① P[1] - P[0] = X[1] + X[2] - Y[1] = 4

② P[2] - P[1] = X[3] - X[2] -Y[2] +Y[1] = -2

③ P[3] - P[2] = X[4] + X[5] - X[1] - Y[3] + Y[2] =3

④ P[4] - P[3] = - X[3] - X[4] + Y[3] - Y[4] = -2

⑤ P[5] - P[4] = - X[5] + Y[4] = -3

觀察發現,每個變數都在兩個式子中出現了,而且一次為正,一次為負。所有等式右邊和為0。接下來,根據上面五個等式構圖。

  • 每個等式為圖中一個頂點,新增源點S和匯點T。
  • 如果一個等式右邊為非負整數c,從源點S向該等式對應的頂點連線一條容量為c,權值為0的有向邊;如果一個等式右邊為負整數c,從該等式對應的頂點向匯點T連線一條容量為c,權值為0的有向邊。
  • 如果一個變數X[i]在第j個等式中出現為X[i],在第k個等式中出現為-X[i],從頂點j向頂點k連線一條容量為∞,權值為V[i]的有向邊。
  • 如果一個變數Y[i]在第j個等式中出現為Y[i],在第k個等式中出現為-Y[i],從頂點j向頂點k連線一條容量為∞,權值為0的有向邊。

構圖以後,求從源點S到匯點T的最小費用最大流,費用值就是結果。

附程式碼:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <queue>
using namespace std;
const int MAXN=500;
const int INF=1684300900;
struct node
{
       int x,y,next,cost,op,d;
}e[MAXN*100];
int first[MAXN*100],pre[MAXN*100];
int n,m,tot,S,T,ans;
int a[MAXN*5],d[MAXN*5];
bool b[MAXN*5];
queue<int> q;
void Add(int x,int y,int c,int d)
{
     e[++tot].y=y;
     e[tot].x=x;
     e[tot].d=d;
     e[tot].cost=c;
     e[tot].op=tot+1;
     e[tot].next=first[x];
     first[x]=tot;
     e[++tot].y=x;
     e[tot].x=y;
     e[tot].d=0;
     e[tot].cost=-c;
     e[tot].op=tot-1;
     e[tot].next=first[y];
     first[y]=tot;
}
void Init()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i)
        scanf("%d",&a[i]);
    for (int i=1;i<=m;++i)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        Add(x,y+1,z,INF);
    }
    S=0,T=n+2;
    for (int i=1;i<=n+1;++i)
    {
        int temp=a[i]-a[i-1];
        if (temp>0) Add(S,i,0,temp); else Add(i,T,0,-temp);
        if (i>1) Add(i,i-1,0,INF);
    }
}
void init()
{
   scanf("%d%d",&n,&m);
   for(int i=1;i<=n;i++)
   scanf("%d",&a[i]);
   for(int i=1;i<=m;i++)
   {
       int x,y,z;
       scanf("%d%d%d",&x,&y,&z);
       Add(x,y+1,z,INF);
   }
   S=0;
   T=n+2;
   for(int i=1;i<=n+1;i++)
   {
       int temp=a[i]-a[i-1];
       if(temp>0) Add(S,i,0,temp);
       else Add(i,T,0,-temp);
       if(i>1) Add(i,i-1,0,INF);
   }

}
bool SPFA()
{
     memset(b,0,sizeof(b));
     memset(d,100,sizeof(d));
     d[S]=0,b[S]=1,q.push(S),pre[S]=-1;
     while (!q.empty())
     {
           int u=q.front();
           b[u]=0,q.pop();
           for (int i=first[u];i;i=e[i].next)
               if (d[e[i].y]>d[u]+e[i].cost && e[i].d>0)
               {
                                            d[e[i].y]=d[u]+e[i].cost;
                                            pre[e[i].y]=i;
                                            if (!b[e[i].y])
                                            {
                                                           b[e[i].y]=1;
                                                           q.push(e[i].y);
                                            }
               }
     }
     return d[T]<INF;
}
void Find()
{
     int Min=INF;
     for (int i=pre[T];i!=-1;i=pre[e[i].x])
         Min=min(Min,e[i].d);
     for (int i=pre[T];i!=-1;i=pre[e[i].x])
     {
         e[i].d-=Min;
         e[e[i].op].d+=Min;
         ans+=Min*e[i].cost;
     }
}
int main()
{   init();
    while (SPFA()) Find();
    printf("%d\n",ans);

return 0;
}