Codeforces Round #384 (Div. 2)E(状压dp+二分,好题)

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.


题意

给定一个序列an,序列中只有1~8的8个整数,让你选出一个子序列,满足下列两个要求

1.不同整数出现的次数相差小于或等于1

2.子序列中整数分布是连续的,即子序列的整数必须是1,1,1....1,2,2,2.....2,2.......连续分布,可以是任意顺序而不要求递增,比如312587644


题解:

首先我们可以二分整数出现的次数,假如说二分出现次数是l,那么是指有些出现l次,而另一些出现l+1次(l越大越优)

由于每个整数只有两种状态,要么选l个,要么选l+1个。

而且选l+1个的个数越多越好。

那么可以将d[i][j]状态定义为选完前i个中状态已经为j(记录已经选了哪些整数种类)时出现次数为l+1的整数的最大个数。


枚举对第k个整数做决策,dp[i][s]就可以转移到另外两个状态

dp[next(i, l)][j|(1<<a[i])] = max(itself, dp[i][j]);

dp[next(i, l+1)][j|(1<<a[i])] = max(itself, dp[i][j]+1);

其中next(i, l)是指从i开始,往后的第l个a[i]的整数位置

然后取dp[1~n][(1<<8)-1]中的最大值,返回答案即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const int inf=0x3fffffff;
const ll mod=1000000007;
const int maxn=1000+100;
int a[maxn];
VI nex[10];
int d[maxn][(1<<8)+100];
int ans;
int n;
int ne[maxn][maxn];
int solve(int l)
{
    for(int i=0;i<=n;i++) for(int j=0;j<(1<<8);j++) d[i][j]=-inf;
    d[0][0]=0;
    int res=-inf;        //res表示长度为l+1的最大数目
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<(1<<8);j++)
        {
            d[i][j]=max(d[i][j],d[i-1][j]);//注意这个,因为d[i][j]指的是到第i个为止,是前i个的情况,而不是以i结尾
            if(j&(1<<a[i])) continue;
            if(ne[i][l]!=-1)
                d[ne[i][l]][j|(1<<a[i])]=max(d[ne[i][l]][j|(1<<a[i])],d[i-1][j]);
            if(ne[i][l+1]!=-1)
                d[ne[i][l+1]][j|(1<<a[i])]=max(d[ne[i][l+1]][j|(1<<a[i])],d[i-1][j]+1);
        }
    }
    if(!l)     //长度为0时特判一下,因为此时最终状态j可能不是(1<<8)-1
    {
        for(int i=1;i<=n;i++)
            for(int j=0;j<(1<<8);j++)
                res=max(res,d[i][j]);
    }
    else
    {
        for(int i=1;i<=n;i++)
            res=max(res,d[i][(1<<8)-1]);
    }
    if(res<0) return -1;
    return res*(l+1)+(8-res)*l;
}
int main()
{
    scanf("%d",&n);
    rep(i,1,n+1)
    {
        scanf("%d",&a[i]);
        a[i]--;
        nex[a[i]].pb(i);
    }
    memset(ne,-1,sizeof(ne));
    for(int i=0;i<8;i++)
    {
        for(int j=0;j<nex[i].size();j++)
        {
            for(int k=j;k<nex[i].size();k++)
            {
                ne[nex[i][j]][k-j+1]=nex[i][k];  //ne数组保存第i个位置的和a[i]值相等的下k个位置,注意d[a[i]][1]=i
            }
        }
    }
    int l=0,r=n/8+1;
    ans=-1;
    while(l<=r)
    {
        int m=(l+r)/2;
        int t=solve(m);
        if(t!=-1)
        {
            ans=max(ans,t);
            l=m+1;
        }
        else r=m-1;
    }
    printf("%d\n",ans);
    return 0;
}









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值