相关链接:
http://www.lydsy.com/JudgeOnline/problem.php?id=1061
题目大意:
输入
输出
仅包含一个整数,表示你所设计的最优方案的总费用。
样例输入
2 3 4
1 2 2
2 3 5
3 3 2
样例输出
提示
1 ≤ N ≤ 1000,1 ≤ M ≤ 10000,题目中其他所涉及的数据均 不超过2^31-1。
设雇佣第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;
}