1283: 序列
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 327 Solved: 185
[Submit][Status][Discuss]
Description
给出一个长度为 的正整数序列Ci,求一个子序列,使得原序列中任意长度为 的子串中被选出的元素不超过K(K,M<=100) 个,并且选出的元素之和最大。
Input
第1行三个数N,m,k。 接下来N行,每行一个字符串表示Ci。
Output
最大和。
Sample Input
10 5 3
4 4 4 6 6 6 6 6 4 4
4 4 4 6 6 6 6 6 4 4
Sample Output
30
HINT
20%的数据:n<=10。
100%的数据:N<=1000,k,m<=100。Ci<=20000。
Source
分析:
和NOI2008志愿者招募很像...有一些区间限制然后求最大(小)费用...
我们可以把所有长度为$m$的子串中选择$k$个元素看成在所有长度为$m$的子串中选$k$次,那么我们连一串$<i-1,i,k,0>$的边来保证每个点最多被选择$k$次,然后连一些$<i,i+m,1,a[i]>$的边来表示当前的点选了之后这个流只能在$i+m$之后的位置再去选择,也就保证了当前子串当前次只选了一个元素,然后跑最大费用最大流就好....
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
//by NeighThorn
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1000+5,maxm=maxn*5;
int n,m,k,S,T,cnt,a[maxn],w[maxm],hd[maxn],to[maxm],fl[maxm],nxt[maxm],vis[maxn],dis[maxn],Min[maxn],from[maxn];
inline void add(int x,int y,int s,int l){
w[cnt]= l,fl[cnt]=s,to[cnt]=y,nxt[cnt]=hd[x],hd[x]=cnt++;
w[cnt]=-l,fl[cnt]=0,to[cnt]=x,nxt[cnt]=hd[y],hd[y]=cnt++;
}
inline bool spfa(void){
memset(dis,inf,sizeof(dis));
memset(Min,inf,sizeof(Min));
queue<int> q;q.push(S),vis[S]=1,dis[S]=0;
while(!q.empty()){
int top=q.front();q.pop();vis[top]=0;
for(int i=hd[top];i!=-1;i=nxt[i])
if(dis[to[i]]>dis[top]+w[i]&&fl[i]){
from[to[i]]=i;
dis[to[i]]=dis[top]+w[i];
Min[to[i]]=min(fl[i],Min[top]);
if(!vis[to[i]])
q.push(to[i]),vis[to[i]]=1;
}
}
return dis[T]!=inf;
}
inline int find(void){
for(int i=T;i!=S;i=to[from[i]^1])
fl[from[i]]-=Min[T],fl[from[i]^1]+=Min[T];
return Min[T]*dis[T];
}
inline int mcmf(void){
int res=0;
while(spfa())
res+=find();
return res;
}
signed main(void){
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
memset(hd,-1,sizeof(hd));
scanf("%d%d%d",&n,&m,&k);
S=0;T=n+1;add(S,1,k,0);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),add(i,i+1,i+1==T?inf:k,0);
for(int i=1;i<=n;i++)
add(i,i+m>n?T:i+m,1,-a[i]);
printf("%d\n",-mcmf());
return 0;
}
By NeighThorn