1. 程式人生 > >[模擬賽] T3 最優序列

[模擬賽] T3 最優序列

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 最優序列