codeForces-743E Vladik and cards(状压+记忆化搜索+二分)

本文解析了CodeForces竞赛E题,介绍了如何通过记录每种数字出现位置、使用二分搜索和状态压缩等方法来解决寻找最长符合条件子序列的问题,并提供了一段完整的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门: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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值