版权声明:蒟蒻Blog随意转载 https://blog.csdn.net/a1799342217/article/details/81562163
费用流
单纯形裸题,然而我不会,只能费用流。
建图很妙。
源点到第一天连边,最后一天的后一天到汇点连边,容量为INF。每一天向后一天连边,容量为INF-a[i]。费用都为0。对于每一类志愿者,连 到 的容量为INF,费用为 的边。注意最后一天之后还要建一个点(因为 )。此时跑出来的费用就是最小费用。
为什么这么建?我们来口胡一下
首先这道题的容量没有上界只有下界。那么我们反过来建就可以跑最大流了。
现在我们要把源点的流量补成INF,那么就要建一些额外边,也就是志愿者的边。因为每一类志愿者只要雇佣就能从
干到
,所以连
到
。
代码:
#include<queue>
#include<cctype>
#include<cstdio>
#include<algorithm>
#define N 1005
#define M 11005
#define F inline
using namespace std;
struct edge{ int nxt,to,d,v,f; }ed[M*100];
struct fat{ int x,e; }fa[N];
int n,m,k=1,s,t,ans,h[N],d[N],rem[N],tmp[N][2];
queue <int> q; bool f[N];
F char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
return l==r?EOF:*l++;
}
F int _read(){
int x=0; char ch=readc();
while (!isdigit(ch)) ch=readc();
while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
return x;
}
F void addedge(int x,int y,int z,int v){
ed[++k]=(edge){h[x],y,z,v,0},h[x]=k;
ed[++k]=(edge){h[y],x,-z,0,0},h[y]=k;
}
F int spfa(){
for (int i=s;i<=t;i++) f[i]=false,d[i]=1e9;
while (!q.empty()) q.pop(); q.push(s),d[s]=0,rem[s]=1e9;
while (!q.empty()){
int x=q.front(); f[x]=false,q.pop();
for (int i=h[x],v;i;i=ed[i].nxt)
if (ed[i].v>ed[i].f&&d[x]+ed[i].d<d[v=ed[i].to]){
d[v]=d[x]+ed[i].d,fa[v].e=i,fa[v].x=x;
rem[v]=min(rem[x],ed[i].v-ed[i].f);
if (!f[v]) q.push(v),f[v]=true;
}
}
if (d[t]==1e9) return 0;
return rem[t];
}
int main(){
n=_read(),m=_read(),t=n+2;
addedge(s,1,0,1e6),addedge(n+1,t,0,1e6);
for (int i=1;i<=n;i++)
addedge(i,i+1,0,1e6-_read());
for (int i=1,p,z;i<=m;i++){
p=_read();
for (int j=1;j<=p;j++)
tmp[j][0]=_read(),tmp[j][1]=_read();
z=_read();
for (int j=1;j<=p;j++)
addedge(tmp[j][0],tmp[j][1]+1,z,1e9);
}
for (int sum;sum=spfa();ans+=d[t]*sum)
for (int x=t,e;x!=s;x=fa[x].x)
ed[e=fa[x].e].f+=sum,ed[e^1].f-=sum;
return printf("%d\n",ans),0;
}