BZOJ1283 - 序列
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;
}
```\]