[模擬賽] T3 最優序列
阿新 • • 發佈:2018-02-28
com 就是 def set 二進制 長度 amp AI freopen
Description
給出一個長度為n(n<=1000)的正整數序列,求一個子序列,使得原序列中任意長度為m的子串中被選出的元素不超過k(k<=m<=10)個,並且選出的元素之和最大。
Input
第一行三個數n,m,k。 第二行n個數,表示各元素數值大小。
Output
一個數,表示最大元素和。
Range
對於10%的數據,n<=10
對於30%的數據,n<=100
對於100%的數據,n<=1000, m從1到10隨機(10的情況比較多)
Solution
看範圍大概可以想到狀壓,這題是把m壓縮一下。我們首先要預處理一下從0到2m裏每個數裏有多少個1,這樣方便以後的判斷。f[i][j]表示以i結尾能取到的最大值,其中j是一個m位二進制數。那麽轉移方程就很好寫了
if(cnt[(j>>1)|(1<<m-1)]<=k)
f[i+1][(j>>1)|(1<<m-1)]=max(f[i+1][(j>>1)|(1<<m-1)],f[i][j]+val[i+1]);
f[i+1][j>>1]=max(f[i+1][j>>1],f[i][j]);
這裏的狀態是反著存的,也就是說:第0位表示i向前第m個數,第1位表示向前m-1個數...第m位表示第i個數。
Code
#include<cstdio>
#include <cstring>
#include<iostream>
#define int long long
using namespace std;
int n,m,k,maxn;
int val[1005];
int cnt[1<<10];
int f[1005][1<<10];
signed main(){
freopen("best.in","r",stdin);
freopen("best.out","w",stdout);
scanf("%lld%lld%lld",&n,&m,&k);maxn=1<<m;
for(int i=1;i<=n;i++) scanf("%lld",&val[i]);
for(int i=0;i<maxn;i++)
cnt[i]=cnt[i>>1]+(i&1);
memset(f,0xcf,sizeof f);
f[0][0]=0;
for(int i=0;i<n;i++){
for(int j=0;j<maxn;j++){
if(f[i][j]<0) continue;
if(cnt[(j>>1)|(1<<m-1)]<=k)
f[i+1][(j>>1)|(1<<m-1)]=max(f[i+1][(j>>1)|(1<<m-1)],f[i][j]+val[i+1]);
f[i+1][j>>1]=max(f[i+1][j>>1],f[i][j]);
}
}
int ans=0;
for(int i=0;i<maxn;i++) ans=max(ans,f[n][i]);
printf("%lld",ans);
return 0;
}
[模擬賽] T3 最優序列