Codeforces Round #384 (Div. 2) E

给出n个数字 1-8之间 要求选出来一个子序列 使里面1-8的数字个数 极差<=1 并且相同数字必须相邻(112 可以但是121不行)求这个子序列的最长长度 

一道状压dp 看不懂别人的dp思想..自己造了一个出来..

首先枚举t 意义是 当前选取子序列 每个数字最少是多少 那么这个子序列中的数字个数为t t+1

dp[i][j]当前在i位 选取数字的情况压缩成j

枚举没选过的数字 用前缀和进行二分查找

从第i位开始 求出res使在满足i-res这个区间有t或者t+1的某数字 进行转移 更新dp[res][j|x] 

当t为0的时候和t>0的时候 一个是 某种数字不选也可以 一个是每种数字都会被选择 所以分开来讨论

前者就是统计出现过的数字的种数

后者 当j|x为11111111 即每种数字都选完的时候 这时候才会满足 数字个数为t或者t+1 才可以进行对ans的更新

时间复杂度 n^2 * (255) * log(n)

一道题补了两天...期间打了好久游戏...QAQ

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<vector>
#include<queue>
#include<malloc.h>
using namespace std;
#define L long long
int n ;
int dp[1050][(1<<8) + 5];
int a[1050];
int b[(1<<8) + 5];
int sum[1050][9];
int main(){
    scanf("%d",&n);
    memset(sum,0,sizeof(sum));
    int ans = 0;
    int mj = (1<<8) - 1;
    for(int i=0;i<=mj;i++){
        int z = i;
        b[z] = 0;
        while(z){
            if(z%2 == 1)b[i] ++;
            z/=2;
        }
    }
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        for(int j=1;j<=8;j++)sum[i][j] = sum[i-1][j];
        sum[i][a[i]] ++ ;
    }
    for(int i=1;i<=8;i++){
        if(sum[n][i]>0)ans++;
    }
    for(int t=1;t*8<=n;t++){
        memset(dp , -1 ,sizeof(dp));
        dp[0][0] = 0;
        for(int i=0;i<n;i++){
            for(int j=0;j<=mj;j++){
                if(dp[i][j] == -1)continue;
                for(int k=1;k<=8;k++){
                    int z = (1<<(k-1));
                    if((z&j) == 0){
                        int g=(z|j);
                        int l = i+1;
                        int r = n;
                        int res = -1;
                        while(l<=r){
                            int mid =(l+r)/2;
                            if(sum[mid][k] - t > sum[i][k]){
                                r = mid - 1;
                            }
                            else if(sum[mid][k] - t < sum[i][k]){
                                l = mid + 1;
                            }
                            else {
                                r = mid - 1;
                                res = mid;
                            }
                        }
                        if(res != -1){
                            if(dp[res][g] == -1 || dp[res][g] < dp[i][j] + t){
                                dp[res][g] = dp[i][j] + t;
                                if(b[g] == 8)
                                    ans = max(dp[res][g] , ans);
                            }
                        }
                        l = i+1;
                        r = n;
                        res = -1;
                        while(l<=r){
                            int mid =(l+r)/2;
                            if(sum[mid][k] - t - 1> sum[i][k]){
                                r = mid - 1;
                            }
                            else if(sum[mid][k] - t - 1< sum[i][k]){
                                l = mid + 1;
                            }
                            else {
                                r = mid - 1;
                                res = mid;
                            }
                        }
                        if(res != -1){
                            if(dp[res][g] == -1 || dp[res][g] < dp[i][j] + t + 1){
                                dp[res][g] = dp[i][j] + t + 1;
                                if(b[g] == 8)
                                    ans = max(dp[res][g] , ans);
                            }
                        }
                    }
                }
            }
        }
    }
    printf("%d\n",ans);
}

  

 

posted @ 2016-12-16 14:14  天翎月  阅读(156)  评论(0编辑  收藏  举报