传送门
思路:
例如一共需要4天,四天需要的人数依次是4,2,5,3。有5类志愿者,如下表所示:
设雇佣第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=0”>i,可以使其变为等式
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.我们将最后的五个等式进一步变形,得出以下结果
① -X[1]-X[2]+Y[1]+4=0
② -X[3]+X[2]+Y[2]-Y[1]-2=0
③ -X[4]-X[5]+X[1]+Y[3]-Y[2]+3=0
④ X[3]+X[4]-Y[3]+Y[4]-2=0
⑤ X[5]-Y[4]-3=0
可 以发现,每个等式左边都是几个变量和一个常数相加减,右边都为0,恰好就像网络流中除了源点和汇点的顶点都满足流量平衡。每个正的变量相当于流入该顶点的 流量,负的变量相当于流出该顶点的流量,而正常数可以看作来自附加源点的流量,负的常数是流向附加汇点的流量。因此可以据此构造网络,求出从附加源到附加 汇的网络最大流,即可满足所有等式。而我们还要求费用最小,所以要在X变量相对应的边上加上权值,然后求最小费用最大流。
接下来,根据上面五个等式构图。
(1)每个等式为图中一个顶点,添加源点S和汇点T。
(2)如果一个等式中的数字为非负整数c,从源点S向该等式对应的顶点连接一条容量为c,权值为0的有向边;如果为负整数-c,从该等式对应的顶点向汇点T连接一条容量为c,权值为0的有向边。
(3)如果一个变量X[i]在第j个等式中出现为-X[i],在第k个等式中出现为+X[i],从顶点j向顶点k连接一条容量为INF,权值为V[i]的有向边。
(4)如果一个变量Y[i]在第j个等式中出现为-Y[i],在第k个等式中出现为+Y[i],从顶点j向顶点k连接一条容量为INF,权值为0的有向边。
构图以后,求从源点S到汇点T的最小费用最大流,费用值就是结果。
当然,我不会单纯形,orzPoPoQQQ
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#define inf 0x3f3f3f3f
#define N 1005
using namespace std;
struct edge{int from,to,next,f,c;}e[N*50];
int head[N],q[N],v[N],from[N],dis[N],d[N];
int n,m,S,T,tot=1,ans;
void add(int x,int y,int f,int c){
e[++tot]=(edge){x,y,head[x],f,c};
head[x]=tot;
e[++tot]=(edge){y,x,head[y],0,-c};
head[y]=tot;
}
bool spfa(){
for (int i=1;i<=T;i++) dis[i]=inf;
int h=dis[S]=0,t=v[S]=1,x; q[1]=S;
while (h!=t){
h=(h+1)%N;
x=q[h];
v[x]=0;
for (int i=head[x];i;i=e[i].next)
if (dis[e[i].to]>dis[x]+e[i].c&&e[i].f){
dis[e[i].to]=dis[x]+e[i].c;
from[e[i].to]=i;
if (!v[e[i].to]){
v[e[i].to]=1;
t=(t+1)%N;
q[t]=e[i].to;
}
}
}
return dis[T]!=inf;
}
void del(){
int x=inf;
for (int i=from[T];i;i=from[e[i].from]) x=min(x,e[i].f);
for (int i=from[T];i;i=from[e[i].from]){
e[i].f-=x; e[i^1].f+=x; ans+=e[i].c*x;
}
}
int main(){
scanf("%d%d",&n,&m);
S=n+2; T=n+3;
for (int i=1;i<=n;i++) scanf("%d",&d[i]);
for (int i=1,x,y,v;i<=m;i++){
scanf("%d%d%d",&x,&y,&v);
add(x,y+1,inf,v);
}
for (int i=1,tmp;i<=n+1;i++){
tmp=d[i]-d[i-1];
if (tmp>=0) add(S,i,tmp,0);
else add(i,T,-tmp,0);
if (i>1) add(i,i-1,inf,0);
}
while (spfa()) del();
printf("%d",ans);
}