https://www.lydsy.com/JudgeOnline/problem.php?id=1061
思路:网络流的建图真的很艺术……给
i
i
i和
i
+
1
i+1
i+1(
1
<
=
i
<
=
n
1<=i<=n
1<=i<=n)连一条权值为
i
n
f
−
a
[
i
]
inf-a[i]
inf−a[i]费用为
0
0
0的边,给每个志愿者连一条从
s
[
i
]
s[i]
s[i]到
t
[
i
]
+
1
t[i]+1
t[i]+1的权值为
i
n
f
inf
inf费用为
c
[
i
]
c[i]
c[i]的边,然后搞一个源点和汇点跑费用流就好了。至于原因嘛,我比较菜也没什么很好的解释,大家可以感性理解一下。因为跑的是费用流,也就是最后得到的一定是最大流,所以第
i
i
i天和
i
+
1
i+1
i+1天之间的流量一定会被带权边补上。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=1010;
const int maxm=12005;
struct Edge
{
int to,nxt,f,w;
}edge[maxm<<1];
int n,m,s,t,tot;
int head[maxn]; //前向星
int dis[maxn],pre[maxn],maxf[maxn],inq[maxn];//dis用于最短路 pre记录前驱边 maxf记录最小剩余容量 inque记录是否在队列中
inline void addedge(int u,int v,int c,int w)//容量为c 权值为w
{
edge[++tot].to=v,edge[tot].f=c,edge[tot].w=w,edge[tot].nxt=head[u],head[u]=tot;
edge[++tot].to=u,edge[tot].f=0,edge[tot].w=-w,edge[tot].nxt=head[v],head[v]=tot;
}
bool spfa()
{
memset(dis,INF,sizeof(dis));
queue<int> q;
q.push(s);
dis[s]=0,inq[s]=1,maxf[s]=INF;
while(!q.empty())
{
int u=q.front();
q.pop(),inq[u]=0;
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(edge[i].f&&dis[v]>dis[u]+edge[i].w)
{
dis[v]=dis[u]+edge[i].w;
pre[v]=i;
maxf[v]=min(maxf[u],edge[i].f);
if(!inq[v])
q.push(v),inq[v]=1;
}
}
}
return dis[t]!=INF;
}
int MCMF()
{
int mcost=0;
while(spfa())
{
int u=t,v;
while(u!=s)
{
v=pre[u];
edge[v].f-=maxf[t];
edge[v^1].f+=maxf[t];
u=edge[v^1].to;
}
mcost+=dis[t]*maxf[t];
}
return mcost;
}
int main()
{
while(~scanf("%d%d%",&n,&m))
{
tot=1;
memset(head,0,sizeof(head));
int u,v,f;
s=0,t=n+2;
addedge(s,1,INF,0);
addedge(n+1,t,INF,0);
for(int i=1;i<=n;i++)
{
scanf("%d",&u);
addedge(i,i+1,INF-u,0);
}
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&u,&v,&f);
addedge(u,v+1,INF,f);
}
printf("%d\n",MCMF());
}
return 0;
}