[NOI2008] [bzoj1061] 志愿者招募
还是一道费用流的题目。话不多说,进入正题。
题意:给定n个点和m种从l到r覆盖一层的费用,求满足所有点的覆盖层数都大等于权值的最小费用
分析:要做到区间修改,看似比较麻烦。
用差分把区间修改变成单点修改(左端+,右端-)
那么建一种边,从右端+1的位置流向左端点的位置,花费为c
然后有可能覆盖层数过大,我们建一种边使满足单点层数-1花费为0,使最后的覆盖结果一定是与要求的一致
最后就是建源到某个点的流量剩余(或这个点到汇的流量需求)
下面贴上代码:
#include<cstdio> using namespace std; const int inf=2147483647; int n,m; int tot=1,mx,q[1010],d[1010],pree[1010],h[1010]; struct edge{int to,nxt,cst,cap;}e[30010]; bool vis[1010]; int mn(int x,int y){return x>y?y:x;} void add(int fr,int to,int cst,int cap) { e[++tot]={to,h[fr],cst,cap};h[fr]=tot; e[++tot]={fr,h[to],-cst,0};h[to]=tot; } void init() { scanf("%d%d",&n,&m); int now,lst=0; for(int i=1;i<=n;i++) { scanf("%d",&now); int cst=now-lst;lst=now; if(cst>0)add(i,n+2,0,cst),mx+=cst;//差分建图 else add(0,i,0,-cst); add(i,i+1,0,inf);//单点减1答案不变 } add(0,n+1,0,lst); for(int i=1;i<=m;i++) { int l,r,c; scanf("%d%d%d",&l,&r,&c); add(r+1,l,c,inf);//区间加法头加尾减 } n+=2; } bool spfa() { for(int i=1;i<=n;i++)d[i]=inf; int l=0,r=1;q[1]=0;vis[0]=1; while(l!=r) { int x=q[l=l==n?0:l+1]; for(int i=h[x];i;i=e[i].nxt) if(e[i].cap&&e[i].cst+d[x]<d[e[i].to]) { int v=e[i].to; pree[v]=i; d[v]=d[x]+e[i].cst; if(!vis[v]) { if(d[v]>d[l+1])q[r=r==n?0:r+1]=v; else q[l]=v,l=l==0?n:l-1; vis[v]=1; } } vis[x]=0; } return d[n]==inf?0:1; } int costflow() { int cost=0,mm=0; while(spfa()) { int mi=inf; for(int i=n;i;i=e[pree[i]^1].to) mi=mn(mi,e[pree[i]].cap); for(int i=n;i;i=e[pree[i]^1].to) { int ee=pree[i]; e[ee].cap-=mi; e[ee^1].cap+=mi; } cost+=d[n]*mi; mm+=mi; } return mm==mx?cost:0; } int main() { init(); printf("%d",costflow()); return 0; }
本文由qrc出品,若不在本博客上看到,请与本人联系。
网址:http://www.cnblogs.com/qrcer