BZOJ1283 - 序列

Portal

Description

给出一个长度为\(n(n\leq1000)\)的正整数序列\(\{c_n\}\),从中选出若干个元素,使得原序列中任意连续\(m(m\leq100)\)个数中被选出的元素不超过\(k(k\leq100)\)个,并且选出的元素之和最大。

Solution

容易知道,这个问题是一个线性规划问题:

\[\begin{align*} max \quad & \sum_{i=1}^n c_ix_i\\ s.t. \quad & x_1+x_2+...+x_m+y_1 = k \\ & x_2+x_3+...+x_{m+1}+y_2 = k \\ &... \\ & x_{n-m+1}+x_{n-m+2}+...+x_n+y_{n-m+1} = k \\ & x_i\in\{0,1\},y_i\geq 0 \end{align*}$$$x_i=1$表示选中了$c_i$这个数。但直接跑这个会`TLE`╮(╯﹏╰)╭ 于是将其差分: $$\begin{align*} max \quad & \sum_{i=1}^n c_ix_i\\ s.t. \quad & x_1+x_2+...+x_m+y_1-k = 0 \\ & x_{m+1}-x_1+y_2-y_1=0 \\ & x_{m+2}-x_2+y_3-y_2=0 \\ & ... \\ & x_n-x_{n-m}+y_{n-m+1}-y_{n-m}=0 \\ & x_i\in\{0,1\},y_i\geq 0 \end{align*}$$然后发现这个是可以转化成网络流的。怎么发现的呢?每个变量都至多有一次加一次减,那么可以把每个等式看做一个点,每个变量看做一条边。如果某个变量在等式$u$中是减,在等式$v$中是加,那么就有边$(u,v)$;如果只在$u$中加,那么有$(s,u)$;如果只在$u$中减,那么有$(u,t)$。对于代表$x_i$的边,其容量为$1$,费用为$c_i$;代表$y_i$得便,容量为$+\infty$,费用为$0$。跑一下最大费用最大流就好啦。 ##Code ```cpp //序列 #include <cstdio> #include <algorithm> #include <queue> using namespace std; 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=0x3F3F3F3F; int n,m,k,a[N]; int s,t; int h[N],cnt; struct edge{int v,c,w,nxt;} ed[N*300]; 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 dst[N],pre[N]; queue<int> Q; bool inQ[N]; bool SPFA() { for(int i=1;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; } int maxCost() { int 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+=fl*dst[t]; } return cost; } int main() { n=read(),m=read(),k=read(); for(int i=1;i<=n;i++) a[i]=read(); s=n-m+2,t=n-m+3; cnt=1; int t1=n-m+1; //针对x建边 if(m+1<=n-m) { for(int i=1;i<=m;i++) edAdd(i,t1,1,a[i]); for(int i=m+1;i<=n-m;i++) edAdd(i,i-m,1,a[i]); for(int i=n-m+1;i<=n;i++) edAdd(s,i-m,1,a[i]); } else { for(int i=1;i<=n-m;i++) edAdd(i,t1,1,a[i]); for(int i=n-m+1;i<=m;i++) edAdd(s,t1,1,a[i]); for(int i=m+1;i<=n;i++) edAdd(s,i-m,1,a[i]); } //针对y建边 for(int i=2;i<=n-m;i++) edAdd(i,i-1,INF,0); edAdd(1,t1,INF,0); edAdd(s,n-m,INF,0); edAdd(t1,t,k,0); //最大费用流 printf("%d\n",maxCost()); return 0; }  ```\]

posted @ 2018-03-19 07:56  VisJiao  阅读(199)  评论(0编辑  收藏  举报