BZOJ 1283: 序列
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