Description
申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要N 天才能完成,其中第i 天至少需要Ai 个人。 布布通过了解得知,一共有M 类志愿者可以招募。其中第i 类可以从第Si 天工作到第Ti 天,招募费用是每人Ci 元。新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这并不是他的特长!于是布布找到了你,希望你帮他设计一种最优的招募方案。Input
Output
仅包含一个整数,表示你所设计的最优方案的总费用。
Sample Input
2 3 4
1 2 2
2 3 5
3 3 2
Sample Output
HINT
1 ≤ N ≤ 1000,1 ≤ M ≤ 10000,题目中其他所涉及的数据均 不超过2^31-1。
分析: 第一眼就看出是一道费用流 那别废话了,建图吧。。。鼓捣了半天,没搞出来。。。 问题就出在志愿者的服务时间是一个区间$L_i$,如果我们雇佣了他们,会在这一段时间内都产生影响 这和普通的网络流是有点区别的
学姐推荐了一个大神链
这道题的模型隐藏得较深,不易想到
下面我们就由浅入深,通过一个例子来说明:
N=4,四天需要的人数分别是4,2,5,3,有五类志愿者,如下表所示:
种类 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
时间 | 1~ 2 | 1 ~ 1 | 2~ 3 | 3~ 3 | 3~4 |
费用 | 3 | 4 | 3 | 5 | 6 |
设雇佣第i类的志愿者人数为 X i X_i Xi,每个志愿者的费用是 V i V_i Vi,第j天雇佣的人数为 P i P_i Pi ,那么每一天雇佣的人数满足一个不等式:
P
1
=
X
1
+
X
2
>
=
4
P_1=X_1+X_2>=4
P1=X1+X2>=4
P
2
=
X
1
+
X
3
>
=
2
P_2=X_1+X_3>=2
P2=X1+X3>=2
P
3
=
X
3
+
X
4
+
X
5
>
=
5
P_3=X_3+X_4+X_5>=5
P3=X3+X4+X5>=5
P
4
=
X
5
>
=
3
P_4=X_5>=3
P4=X5>=3
然而这些不等式我们没法解啊
于是我们对于每一个个不等式,添加一个辅助变量
Y
i
Y_i
Yi(
Y
i
>
=
0
Y_i>=0
Yi>=0),可以使其变成等式:
P
1
=
X
1
+
X
2
−
Y
1
=
4
P_1=X_1+X_2-Y_1=4
P1=X1+X2−Y1=4
P
2
=
X
1
+
X
3
−
Y
2
=
2
P_2=X_1+X_3-Y_2=2
P2=X1+X3−Y2=2
P
3
=
X
3
+
X
4
+
X
5
−
Y
3
=
5
P_3=X_3+X_4+X_5-Y_3=5
P3=X3+X4+X5−Y3=5
P
4
=
X
5
−
Y
4
=
3
P_4=X_5-Y_4=3
P4=X5−Y4=3
在上述四个等式上下添加 P 0 = 0 , P 5 = 0 P_0=0,P_5=0 P0=0,P5=0,每次用下边的式子减去上边的式子,得:
P
0
=
0
P_0=0
P0=0
P
1
=
X
1
+
X
2
−
Y
1
=
4
P_1=X_1+X_2-Y_1=4
P1=X1+X2−Y1=4
P
2
=
X
1
+
X
3
−
Y
2
=
2
P_2=X_1+X_3-Y_2=2
P2=X1+X3−Y2=2
P
3
=
X
3
+
X
4
+
X
5
−
Y
3
=
5
P_3=X_3+X_4+X_5-Y_3=5
P3=X3+X4+X5−Y3=5
P
4
=
X
5
−
Y
4
=
3
P_4=X_5-Y_4=3
P4=X5−Y4=3
P
5
=
0
P_5=0
P5=0
P
1
−
P
0
=
X
1
+
X
2
−
Y
1
=
4
P_1-P_0=X_1+X_2-Y_1=4
P1−P0=X1+X2−Y1=4 ①
P
2
−
P
1
=
X
3
−
X
2
−
Y
2
+
Y
1
=
−
2
P_2-P_1=X_3-X_2-Y_2+Y_1=-2
P2−P1=X3−X2−Y2+Y1=−2 ②
P
3
−
P
2
=
X
4
+
X
5
−
X
1
−
Y
3
+
Y
2
=
3
P_3-P_2=X_4+X_5-X_1-Y_3+Y_2=3
P3−P2=X4+X5−X1−Y3+Y2=3 ③
P
4
−
P
3
=
−
X
3
−
X
4
+
Y
3
−
Y
4
=
−
2
P_4-P_3=-X_3-X_4+Y_3-Y_4=-2
P4−P3=−X3−X4+Y3−Y4=−2 ④
P
5
−
P
4
=
−
X
5
+
Y
4
=
−
3
P_5-P_4=-X_5+Y_4=-3
P5−P4=−X5+Y4=−3 ⑤
我们可以发现,每个变量都在两个式子中出现了,而且一次为正,一次为负
所有等式和为0
接下来根据上面五个等式构图
- 每个等式为图中一个顶点,添加源点 S S S和汇点 T T T
- 如果一个等式右边为非负整数
c
c
c,从源点
S
S
S向该等式对应的顶点连一条容量为
c
c
c,权值为
0
0
0的有向边
如果一个等式右边为负整数 c c c,从给等式对应的顶点向汇点连一条容量为 c c c,权值为 0 0 0的有向边 - 如果一个变量 X i X_i Xi在第 j j j 个等式中出现,在第 k k k个等式中出现为 − X i -X_i −Xi,从顶点 j j j向顶点 k k k连一条容量为 ∞ ∞ ∞,权值为 V i V_i Vi的有向边
- 如果一个变量 Y i Y_i Yi在第 j j j 个等式中出现,在第 k k k个等式中出现为 − Y i -Y_i −Yi,从顶点 j j j向顶点 k k k连一条容量为 ∞ ∞ ∞,权值为 0 0 0的有向边
求从源点
S
S
S到汇点
T
T
T的最小费用最大流,费用值就是结果
上面的方式很神奇的求出了结果,我们解释一下为什么这样构图
我们将最后五个等式进一步变形,得:
−
X
1
−
X
2
+
Y
1
+
4
=
0
-X_1-X_2+Y_1+4=0
−X1−X2+Y1+4=0 ①
−
X
3
+
X
2
+
Y
2
−
Y
1
−
2
=
0
-X_3+X_2+Y_2-Y_1-2=0
−X3+X2+Y2−Y1−2=0 ②
−
X
4
−
X
5
+
X
1
+
Y
3
−
Y
2
+
3
=
0
-X_4-X_5+X_1+Y_3-Y_2+3=0
−X4−X5+X1+Y3−Y2+3=0 ③
X
3
+
X
4
−
Y
3
+
Y
4
−
2
=
0
X_3+X_4-Y_3+Y_4-2=0
X3+X4−Y3+Y4−2=0 ④
X
5
−
Y
4
−
3
=
0
X_5-Y_4-3=0
X5−Y4−3=0 ⑤
可以发现,每个等式左边都是几个变量和一个常数相加减,右边都为0,恰好就像网络流中除了源点和汇点的顶点都满足流量平衡
每个正的变量相当于流入该顶点的流量,负的变量相当于流出该顶点的流量,而正常数可以看作来自附加源点的流量,负常数是流向附加汇点的流量
因此可以据此构造网络,求出从附加源到附加汇的网络最大流,即可满足所有等式
而我们还要求
Σ
(
V
i
∗
X
i
)
Σ(V_i*X_i)
Σ(Vi∗Xi)最小,所以要在X变量相对应的边上加上权值,然后求最小费用最大流
在建图的时候,我们能发现规律:
等式右边的值就是相邻两天的A值之差
X
i
X_i
Xi的正值出现在
s
t
a
r
t
i
start_i
starti,负出现在
e
n
d
i
+
1
end_i+1
endi+1
Y
i
Y_i
Yi的正值出现在
i
+
1
i+1
i+1,负值出现在
i
i
i
#tip
不能不说,这道题xue微有点难很难,
难就难在数学模型的抽象,而这就是信息学的精髓所在
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
const int INF=0x33333333;
const int N=10005;
int n,m,A[N],C[N],st[N],tot=-1;
struct node{
int x,y,v,c,nxt;
};
node way[N*100];
int dis[N],s,t,pre[N];
bool in[N];
void add(int u,int w,int z,int cc)
{
tot++;
way[tot].x=u;way[tot].y=w;way[tot].v=z;way[tot].c=cc;way[tot].nxt=st[u];st[u]=tot;
tot++;
way[tot].x=w;way[tot].y=u;way[tot].v=0;way[tot].c=-cc;way[tot].nxt=st[w];st[w]=tot;
}
int spfa(int s,int t)
{
queue<int> Q;
memset(dis,0x33,sizeof(dis));
memset(in,0,sizeof(in));
Q.push(s); dis[s]=0; in[s]=1;
while (!Q.empty())
{
int now=Q.front(); Q.pop();
in[now]=0;
for (int i=st[now];i!=-1;i=way[i].nxt)
if (way[i].v&&dis[way[i].y]>dis[now]+way[i].c)
{
dis[way[i].y]=dis[now]+way[i].c;
pre[way[i].y]=i;
if (!in[way[i].y])
{
in[way[i].y]=1;
Q.push(way[i].y);
}
}
}
return dis[t]<INF;
}
int doit()
{
int ans=0;
while (spfa(s,t))
{
int sum=INF;
for (int i=t;i!=s;i=way[pre[i]].x)
sum=min(sum,way[pre[i]].v);
ans+=sum*dis[t];
for (int i=t;i!=s;i=way[pre[i]].x)
way[pre[i]].v-=sum,
way[pre[i]^1].v+=sum;
}
return ans;
}
int main()
{
memset(st,-1,sizeof(st));
scanf("%d%d",&n,&m);
s=0; t=n+2;
for (int i=1;i<=n;i++)
{
scanf("%d",&A[i]);
if (A[i]-A[i-1]>0) add(s,i,A[i]-A[i-1],0);
else add(i,t,A[i-1]-A[i],0);
add(i+1,i,INF,0);
}
add(n+1,t,A[n],0);
for (int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y+1,INF,z);
}
printf("%d\n",doit());
return 0;
}