BZOJ1061 - [NOI2008]志愿者招募

Portal

Description

实行一个\(n(n\leq10^3)\)天的项目,其中第\(i\)天至少需要\(b_i\)个人。一共有\(m(m\leq10^4)\)类志愿者可以招募,其中第\(i\)类可以从第\(s_i\)天工作到第\(t_i\)天,招募费用是每人\(c_i\)元。求最小花费的招募方案。

Solution1

根据题意,很容易列出一个线性规划:

\[\begin{align} min \quad & \sum_{i=1}^m c_ix_i \\ s.t. \quad & \sum_{i=1}^m a_{ji}x_i \geq b_j \\ & x_i\geq0 \end{align} $$其中$x_i$代表第$i$类志愿者的个数,$a_{ji}=1$当且仅当$s_i\leq j \leq t_i$。 该线性规划可转化为其对偶问题: \]

\begin{align}
max \quad & \sum_{j=1}^n b_jy_j \
s.t. \quad & \sum_{j=1}^n a_{ji}y_j \leq c_i \
& y_j\geq0
\end{align}

\[用单纯形求解即可。 ##Solution2 还可以用费用流求解。按如下方式建图: - $s=0,t=n+1$,共$n+2$个点; - 连边$(s,1)$,容量为足够大的数$U$,费用为$0$; - 连边$(i,i+1)$,容量为$U-b_i$,费用为$0$; - 对于第$i$类志愿者,连边$(s_i,t_i+1)$,容量为$+\infty$,费用为$c_i$。 第$i$类志愿者代表的边每有$1$的流量,原图$s_i$到$t_i$就可以少$1$的流量,就相当于招募了一个志愿者。 ##Code ```cpp //[Noi2008]志愿者招募 #include <cstdio> inline char gc() { static char now[1<<16],*S,*T; if(S==T) {T=(S=now)+fread(now,1,1<<16,stdin); if(S==T) return EOF;} return *S++; } inline int read() { int x=0; char ch=gc(); while(ch<'0'||'9'<ch) ch=gc(); while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc(); return x; } int const N=1e3+10; double const EPS=1e-8; bool equal0(double x) {return -EPS<x&&x<EPS;} int n,m; double a[N*10][N]; void pivot(int x,int y) { double t=a[x][y]; a[x][y]=1; for(int i=0;i<=n;i++) a[x][i]/=t; for(int i=0;i<=m;i++) { if(i==x||equal0(a[i][y])) continue; t=a[i][y]; a[i][y]=0; for(int j=0;j<=n;j++) a[i][j]-=a[x][j]*t; } } void simplex() { while(true) { int x=0,y=0; double minX=1e18; for(int i=1;!y&&i<=n;i++) if(a[0][i]>EPS) y=i; if(!y) return; for(int i=1;i<=m;i++) if(a[i][y]>EPS&&a[i][0]/a[i][y]<minX) minX=a[i][0]/a[i][y],x=i; if(!x) return; pivot(x,y); } } int main() { n=read(),m=read(); for(int i=1;i<=n;++i) a[0][i]=read(); for(int i=1;i<=m;++i) { int x=read(),y=read(); for(int j=x;j<=y;++j) a[i][j]=1; a[i][0]=read(); } simplex(); printf("%lld\n",-(long long)a[0][0]); return 0; } ``` ```cpp //[Noi2008]志愿者招募 #include <cstdio> #include <algorithm> #include <queue> using namespace std; typedef long long lint; inline char gc() { static char now[1<<16],*S,*T; if(S==T) {T=(S=now)+fread(now,1,1<<16,stdin); if(S==T) return EOF;} return *S++; } inline int read() { int x=0; char ch=gc(); while(ch<'0'||'9'<ch) ch=gc(); while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc(); return x; } int const N=1e3+10; int const INF=0x7FFFFFFF; int n,m; int s,t; int h[N],cnt; struct edge{int v,c,w,nxt;} ed[N*30]; void edAdd(int u,int v,int c,int w) { cnt++; ed[cnt].v=v,ed[cnt].c=c,ed[cnt].w=w,ed[cnt].nxt=h[u],h[u]=cnt; cnt++; ed[cnt].v=u,ed[cnt].c=0,ed[cnt].w=-w,ed[cnt].nxt=h[v],h[v]=cnt; } int pre[N],dst[N]; queue<int> Q; bool inQ[N]; bool SPFA() { for(int i=s;i<=t;i++) dst[i]=INF,pre[i]=0; dst[s]=0; Q.push(s),inQ[s]=true; while(!Q.empty()) { int u=Q.front(); Q.pop(),inQ[u]=false; for(int i=h[u];i;i=ed[i].nxt) { int v=ed[i].v,w=ed[i].w; if(ed[i].c&&dst[u]+w<dst[v]) { dst[v]=dst[u]+w,pre[v]=i; if(!inQ[v]) Q.push(v),inQ[v]=true; } } } return dst[t]<INF; } lint netFlow() { lint cost=0; while(SPFA()) { int fl=INF; for(int i=pre[t];i;i=pre[ed[i^1].v]) fl=min(fl,ed[i].c); for(int i=pre[t];i;i=pre[ed[i^1].v]) ed[i].c-=fl,ed[i^1].c+=fl; cost+=(lint)fl*dst[t]; } return cost; } int main() { n=read(),m=read(); s=0,t=n+1; cnt=1; edAdd(s,1,INF,0); for(int i=1;i<=n;i++) edAdd(i,i+1,INF-read(),0); for(int i=1;i<=n;i++) { int s=read(),t=read(),w=read(); edAdd(s,t+1,INF,w); } printf("%lld\n",netFlow()); return 0; } ``` ##P.S. 感觉对偶问题不大好理解呀...\]

posted @ 2018-03-18 19:35  VisJiao  阅读(236)  评论(0编辑  收藏  举报