[Noi2008]志願者招募(BZOJ1061)
相關連結:
http://www.lydsy.com/JudgeOnline/problem.php?id=1061
題目大意:
申奧成功後,布布經過不懈努力,終於成為奧組委下屬公司人力資源部門的主管。布布剛上任就遇到了一個難 題:為即將啟動的奧運新專案招募一批短期志願者。經過估算,這個專案需要N 天才能完成,其中第i 天至少需要 Ai 個人。 布布通過了解得知,一共有M 類志願者可以招募。其中第i 類可以從第Si 天工作到第Ti 天,招募費用 是每人Ci 元。新官上任三把火,為了出色地完成自己的工作,布布希望用盡量少的費用招募足夠的志願者,但這 並不是他的特長!於是布布找到了你,希望你幫他設計一種最優的招募方案。輸入
輸出
僅包含一個整數,表示你所設計的最優方案的總費用。
樣例輸入
3 32 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;
}