CodeForces743E. Vladik and cards 二分+装压dp

Posted TS_Hugh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CodeForces743E. Vladik and cards 二分+装压dp相关的知识,希望对你有一定的参考价值。

这个题我们可以想象成_---___-----__的一个水柱它具有一遍优一遍行的性质因此可以用来二分最小值len,而每次二分后我们都要验根,we可以把这个水柱想成我们在每个数段里取前一段的那个数后一段有也不选,而且最后一个区间的第一个数一定可以使这个数区间对应的数,那么我们只要在某个位置上不选或选就可以啦,这we很容易想到2n的验根但是这样还不如打暴力,这时我们发现这个dfs是低效的他会很多进入等效状态这样的话我们就可以记忆化搜索了,那么何妨递推,由于我们知道f[pos][mask]中只要pos(位置),mask(二进制代表是否选过)确定那么就要他的最大值就行了,而状态转移分两种一是前面len或len+1,或前一个状态,那么只要每一个状态是最大值,那么我们就从前面推到最后就找到了,这样的话我们可以正向推来代替反向吸收,因为如果只有那样吸收也就只有那样推.

千万不要忘了0的特判以及状态转移的完全性.

时间复杂度o(8*log2n*28*n)

#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 1001
using namespace std;
int f[MAXN][(1<<8)+10];
int n,a[MAXN],full=(1<<8)-1;
bool had[10];
vector<int>pos[10];
inline void pre()
{
   scanf("%d",&n);
   for(int i=1;i<=n;i++)
   {
    scanf("%d",&a[i]);
    had[a[i]]=1;
    pos[a[i]].push_back(i);
   }
}
inline int Max(int x,int y)
{
   return x>y?x:y;
}
inline int get(int p,int len)
{
   int now=lower_bound(pos[a[p]].begin(),pos[a[p]].end(),p)-pos[a[p]].begin();
   int ans=now+len-1;
   if(pos[a[p]].size()-1<ans)return -1;
   return pos[a[p]][ans];
}
int judge(int len)
{
   memset(f,0,sizeof(f));
   for(int i=0;i<n;i++)
   {
      int to=get(i+1,len);
      if(to!=-1)f[to][(1<<(a[i+1]-1))]=Max(f[i][0]+len,f[to][(1<<(a[i+1]-1))]);
      to=get(i+1,len+1);
      if(to!=-1)f[to][(1<<(a[i+1]-1))]=Max(f[i][0]+len+1,f[to][(1<<(a[i+1]-1))]);
      for(int j=1;j<full;j++)
        if(f[i][j])
        {
          f[i+1][j]=Max(f[i+1][j],f[i][j]);
          if(j&(1<<(a[i+1]-1)))continue;
          to=get(i+1,len);
          if(to!=-1)f[to][j|(1<<(a[i+1]-1))]=Max(f[i][j]+len,f[to][j|(1<<(a[i+1]-1))]);
          to=get(i+1,len+1);
          if(to!=-1)f[to][j|(1<<(a[i+1]-1))]=Max(f[i][j]+len+1,f[to][j|(1<<(a[i+1]-1))]);
        }
     f[i+1][full]=Max(f[i+1][full],f[i][full]);
    }
   return f[n][full];
}
void work()
{
   int ans=0,l=1,r=n>>3;
   for(int i=1;i<=8;i++)
    if(had[i])ans++;
   while(l<=r)
   {
     int mid=(l+r)>>1,x=judge(mid);
     ans=Max(x,ans);
     if(x)
      l=mid+1;
     else
      r=mid-1;
   }
   printf("%d",ans);
}
int main()
{
   pre();
   work();
   return 0;
}

 

以上是关于CodeForces743E. Vladik and cards 二分+装压dp的主要内容,如果未能解决你的问题,请参考以下文章

CodeForces 743A Vladik and flights (水题)

Codeforces Round #384 (Div. 2) E. Vladik and cards 状压dp

[CF743E] Vladik and cards

(线段树+并查集) Codeforces Round #416 (Div. 2) E Vladik and Entertaining Flags

Codeforces Round #416 (Div. 2) 811D Vladik and Favorite Game

codeforces 743E