[NOI2008]志愿者招募
http://www.lydsy.com/JudgeOnline/problem.php?id=1061
有n天,每天都需要一定数量的人。有m种志愿者,第i个志愿者可以在si到ti天工作,但需要费用ci,求最小费用。
有上下界无源汇费用流。
开一个超级S,超级T,然后每个点计算一下入度与出度之差,f[i]=in[i]-out[i]
如果>0 向S连一条流量f[i],费用为0的边。
如果<0 向T连一条流量-f[i],费用为0的边。
这道题中,对每一个志愿者,从si向ti+1的点连费用为ci,流量INF的边。
然后每一个点向前一个点连流量INF,费用为0的边。
然后跑一遍费用流~
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #define S 0 #define T 1002 #define MAXN 1005 #define MAXL 10000 #define INF 2000000000 using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();} return x*f; } int n,m,cnt=1,ans=0; int head[MAXN+5]; int in[MAXN+5]; int out[MAXN+5]; int d[MAXN+5]; bool mark[MAXN+5]; queue<int> q; struct edge{ int to,next,w,c; }e[MAXL*5]; void ins(int f,int t,int w,int c) { e[++cnt].next=head[f]; head[f]=cnt; e[cnt].to=t; e[cnt].w=w; e[cnt].c=c; } int dfs(int x,int f) { int used=0;mark[x]=1; if(x==T) return f; for(int i=head[x];i;i=e[i].next) { int v=e[i].to; if(!mark[v]&&d[v]==d[x]-e[i].c&&e[i].w) { int w=dfs(v,min(f-used,e[i].w)); ans+=w*e[i].c; e[i].w-=w; e[i^1].w+=w; used+=w; if(used==f) return f; } } return used; } bool bfs() { q.push(T); for(int i=0;i<T;i++) d[i]=INF; memset(mark,0,sizeof(mark)); d[T]=0;mark[T]=1; while(!q.empty()) { int u=q.front();q.pop(); for(int i=head[u];i;i=e[i].next) { int v=e[i].to; if(d[u]+e[i^1].c<d[v]&&e[i^1].w) { d[v]=d[u]+e[i^1].c; if(!mark[v]) { mark[v]=1;q.push(v); } } } mark[u]=0; } return d[S]!=INF; } inline void insw(int f,int t,int w,int c) { ins(f,t,w,c);ins(t,f,0,-c); } int main() { n=read();m=read();int pre=0; for(int i=1;i<=n;i++) { int x=read(); if(x>pre) insw(S,i,x-pre,0); if(x<pre) insw(i,T,pre-x,0); pre=x; insw(i+1,i,INF,0); } insw(n+1,T,pre,0); for(int i=1;i<=m;++i) { int x=read(),y=read(),c=read(); insw(x,y+1,INF,c); } while(bfs()) { mark[T]=1; while(mark[T]) { memset(mark,0,sizeof(mark)); dfs(S,INF); } } cout<<ans; return 0; }
FallDream代表秋之国向您问好!
欢迎您来我的博客www.cnblogs.com/FallDream