费用流
https://www.luogu.org/problemnew/show/P3980
源点连第一天
汇点连最后一天
容量为INF费用为0
这样跑网络流是沿时间流的(就是依次解决每一天的问题)
然后每一天向后一天连一条容量为INF-a[i]
费用为0的边
为什么容量为INF-a[i]
这就相当于少了a[i]
得用带权边补全INF
这就是志愿者连续干时沿这条边跑
因为连续干不花钱
所以优先选这种边
然后将每一类志愿者s[i]与t[i]+1连一条容量为
INF花费为c[i]的边
当连续干的人不够时
就得 使劲往里塞人
补全INF
#include<bits/stdc++.h> using namespace std; const int M=2e3+3; const int inf=0x3f3f3f3f; struct node{ int u,v,w,cost,nextt; }e[30000]; int mincost; int head[M],cur[M],dis[M],vis[M],a[M],tot,s,t; void addedge(int u,int v,int w,int cost){ e[tot].u=u; e[tot].v=v; e[tot].w=w; e[tot].cost=cost; e[tot].nextt=head[u]; head[u]=tot++; e[tot].u=v; e[tot].v=u; e[tot].w=0; e[tot].cost=-cost; e[tot].nextt=head[v]; head[v]=tot++; } bool bfs(){ for(int i=0;i<=t;i++) dis[i]=inf; queue<int>que; que.push(s); dis[s]=0; while(!que.empty()){ int u=que.front(); que.pop(); vis[u]=0; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(e[i].w&&dis[u]+e[i].cost<dis[v]){ dis[v]=dis[u]+e[i].cost; if(!vis[v]){ vis[v]=1; que.push(v); } } } } return dis[t]!=inf; } int dfs(int u,int fl){ if(u==t) return fl; int ans=0; vis[u]=1; for(int i=cur[u];~i;i=e[i].nextt){ int v=e[i].v; if(dis[v]==dis[u]+e[i].cost&&!vis[v]&&e[i].w){ cur[u]=i; int x=dfs(v,min(e[i].w,fl-ans)); e[i].w-=x; e[i^1].w+=x; ans+=x; mincost+=x*e[i].cost; if(ans==fl) break; } } vis[u]=0; return ans; } void MCMF(){ while(bfs()){ for(int i=0;i<=t;i++) cur[i]=head[i]; dfs(s,inf); } } int main(){ int n,m; scanf("%d%d",&n,&m); memset(head,-1,sizeof(head)); s=0,t=n+2; for(int i=1;i<=n;i++) scanf("%d",&a[i]),addedge(i,i+1,inf-a[i],0); addedge(0,1,inf,0); addedge(n+1,t,inf,0); while(m--){ int l,r,cost; scanf("%d%d%d",&l,&r,&cost); addedge(l,r+1,inf ,cost); } MCMF(); printf("%d",mincost); return 0; }