传送门:http://codeforces.com/contest/743/problem/E
题意:有n个元素的序列,每个元素均是[1,8]中的整数,要求找出他的最长子序列满足以下2个条件:①在这个序列中,[1,8]每个数字出现的次数之差的绝对值不超过1;②相同的数字必须连续.输出最长子序列的长度
题解:①对于每种数字保存他出现时的下标;②二分每种数字至少出现x次,因此每种元素可能出现的次数为x或x+1;③因为一共只有8种数字,因此可以状压为二进制;④当搜索到第i个数字,状态为S时,如果(S>>a[i])&1==0则表示a[i]还没有被选择,因此有2种决策:将a[i]放入子序列或跳过a[i];如果(S>>a[i])&1==1则只能跳过a[i];⑤用dp[i][S]记录状态剪枝
#include<cstdio>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
const int MX = 1e3 + 5;
const int MM = (1<<8);
int a[MX],dp[MX][MM];
int ans,n,inf;
vector<int>v[8];
int dfs(int i,int S,int x){
if(dp[i][S]!=inf) return dp[i][S];//如果已经搜索过改状态这直接返回
if(i>n){
if(S==MM-1) return 0;//成功找到子序列,返回0
return inf;//搜索失败,返回inf
}
int ret=inf;
if(((S>>a[i])&1)==0){
int pos=lower_bound(v[a[i]].begin(),v[a[i]].end(),i)-v[a[i]].begin();
if(pos+x-1<v[a[i]].size()){
ret=max(ret,x+dfs(v[a[i]][pos+x-1]+1,S|(1<<a[i]),x));
}
if(pos+x<v[a[i]].size()){
ret=max(ret,x+1+dfs(v[a[i]][pos+x]+1,S|(1<<a[i]),x));
}
}
ret=max(ret,dfs(i+1,S,x));
return dp[i][S]=ret; //更新已经搜索过的状态dp[i][S]
}
bool ok(int mid){
memset(dp,-0x3f,sizeof(dp));
inf=dp[0][0];
int tmp=dfs(1,0,mid);
ans=max(ans,tmp);
if(tmp>0) return 1;
return 0;
}
int main(){
// freopen("in.txt","r",stdin);
while(~scanf("%d",&n)){
for(int i=0;i<8;i++) v[i].clear();
int mark=0;
ans=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
a[i]--;
if(!((mark>>a[i])&1)) {ans++;mark|=(1<<a[i]);}
v[a[i]].push_back(i);
}
int l=1,r=n/8;
while(l<=r){
int mid=(l+r)>>1;
if(ok(mid)) l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
}
return 0;
}