[codeforces743E]Vladik and cards

E. Vladik and cards
time limit per test 
2 seconds
memory limit per test 
256 megabytes
input standard input
output 
standard output

Vladik was bored on his way home and decided to play the following game. He took n cards and put them in a row in front of himself. Every card has a positive integer number not exceeding 8 written on it. He decided to find the longest subsequence of cards which satisfies the following conditions:

  • the number of occurrences of each number from 1 to 8 in the subsequence doesn't differ by more then 1 from the number of occurrences of any other number. Formally, if there are ck cards with number k on them in the subsequence, than for all pairs of integers  the condition |ci - cj| ≤ 1 must hold.
  • if there is at least one card with number x on it in the subsequence, then all cards with number x in this subsequence must form a continuous segment in it (but not necessarily a continuous segment in the original sequence). For example, the subsequence [1, 1, 2, 2] satisfies this condition while the subsequence [1, 2, 2, 1] doesn't. Note that [1, 1, 2, 2] doesn't satisfy the first condition.

Please help Vladik to find the length of the longest subsequence that satisfies both conditions.

Input

The first line contains single integer n (1 ≤ n ≤ 1000) — the number of cards in Vladik's sequence.

The second line contains the sequence of n positive integers not exceeding 8 — the description of Vladik's sequence.

Output

Print single integer — the length of the longest subsequence of Vladik's sequence that satisfies both conditions.

Examples
input
3
1 1 1
output
1
input
8
8 7 6 5 4 3 2 1
output
8
input
24
1 8 1 2 8 2 3 8 3 4 8 4 5 8 5 6 8 6 7 8 7 8 8 8
output
17
Note

In the first sample all the numbers written on the cards are equal, so you can't take more than one card, otherwise you'll violate the first condition.

题解:

先简单翻译一下,给一个序列,求最长的满足下面条件的子序列:

第一,相同数字连续;第二,每种数字出现次数之差不超过1

我们来考虑,由于每种数字出现都是连续的,因此一种数字一旦出现过,就不能再出现第二次。

所以我们可以用二进制来压每种数字是否出现过。

那么,每种数字出现次数的限制怎么处理?

这个东西不好说,所以我们考虑,如果有一种选择,使得每种数字都出现了至少a次,

那么一定会有其他选择,使得每种数字都出现了至少a-1次,a-2次……1次。

因此,我们就可以二分了!二分枚举每种数字至少出现的次数len,那么每种数字要么出现len次,要么出现len+1次。

对于某个len,定义状态数组f[i][j]为前i位中,数字出现状态为j时出现len+1次的数的最大种数

设tmp=max{f[i][(1<<8)-1}},那么显然,ans=tmp*(len+1)+(8-tmp)*len

在选取新的数字时,新数字要么出现len次,要么出现len+1次,

那么状态方程也显而易见了(刷表),更新对应位置的f值即可

最后注意特判:如果二分得到len=0,那么ans=出现的数的种数

代码见下:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<vector>
 4 #include<algorithm>
 5 using namespace std;
 6 const int N=1010;
 7 const int K=(1<<8)+10;
 8 int n,a[N],f[N][K],now[10],bit[15];//now数组用来记录转移位置 
 9 inline int max(int a,int b){return a>b?a:b;}
10 vector<int> loc[10];
11 inline int judge(int len)
12 {
13     for(int i=1;i<=8;i++)now[i]=0;
14     memset(f,0xaf,sizeof(f));
15     int inf=f[0][0];
16     f[0][0]=0;
17     for(int i=0;i<n;i++)
18     {
19         for(int j=0;j<bit[8];j++)
20         {
21             if(f[i][j]==inf)continue;
22             for(int k=0;k<8;k++)
23             {
24                 if(j&bit[k])continue;
25                 int pos=now[k+1]+len-1;
26                 if(pos>=loc[k+1].size())continue;
27                 f[loc[k+1][pos]][j|bit[k]]=max(f[loc[k+1][pos]][j|bit[k]],f[i][j]);
28                 pos++;
29                 if(pos>=loc[k+1].size())continue;
30                 f[loc[k+1][pos]][j|bit[k]]=max(f[loc[k+1][pos]][j|bit[k]],f[i][j]+1);
31             }
32         }
33         now[a[i]]++;
34     }
35     int ans=inf;
36     for(int i=0;i<=n;i++)
37         ans=max(ans,f[i][bit[8]-1]);
38     if(ans==inf)return -1;
39     return ans*(len+1)+(8-ans)*len;
40 }
41 int main()
42 {
43     bit[0]=1;for(int i=1;i<=10;i++)bit[i]=bit[i-1]<<1;
44     scanf("%d",&n);
45     for(int i=1;i<=n;i++)
46         scanf("%d",&a[i]),loc[a[i]].push_back(i);
47     int l=0,r=n/8+1,ans=2333;
48     while(l<=r)
49     {
50         int mi=(l+r)>>1;
51         if(judge(mi)!=-1)ans=judge(mi),l=mi+1;
52         else r=mi-1;
53      }
54      if(ans==2333)
55      {
56         ans=0;
57          for(int i=1;i<=8;i++)
58              if(!loc[i].empty())ans++;
59     }
60     printf("%d",ans);
61 }
codeforces743E
原文地址:https://www.cnblogs.com/LadyLex/p/7106130.html