Codeforces Round #384 (Div. 2) E. Vladik and cards 状压dp
Codeforces Round #384 (Div. 2) E. Vladik and cards 状压dp
大致题意:
给定一个序列an,序列中只有1~8的8个整数,让你选出一个子序列,子序列应该满足下列两个要求
1.子序列中不同整数出现的次数极差小于或等于1
2.子序列中整数分布是连续的,即子序列的相同整数必须分布在一起, 如 2 2 2 2 1 1 1 1 3 3 3 等.
求出符合要求的子序列的最大长度.
题解:
嗯,对于第一个条件,可以知道的是对于每个符合条件的子序列而言,其中的每个整数的出现的次数最多会有2个不同的值,
比如说1出现了 num 次,那么2只可能出现 num 次或者出现 num+1 次.
对于第二个条件,可以强制使得数字 i 全部出现在一起, 那么我们有必要先预处理出这样一个数组next[i][j][k] 来完成这个过程, next[i][j][k] 表示 从原数组的第i 个位置开始, 大小为j 的数出现次数为k的那个位置.
好了, 当我们知道了num 的值后,我们对于每个数1~8 只要枚举他们长度为 0 (不选), num , num+ 1 这 3 种情况就可以了, 于是我们设DP[i][j] 表示 8个数的选择情况为i,长度为num+1的数字个数为j时可以达到的最远的位置,
那么当我们DP完了以后,只要枚举DP[255][j] (j = 1 .. 8) , 如果 DP[255][j] <= n + 1(结束位置在原数组内部) ,就更新答案......
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn = 1050; 4 int a[maxn],dp[maxn][9],next[maxn][9][maxn],n,cnt[maxn],ans = 0; 5 inline int ask(int k){return (1 << (k - 1));} 6 int main(){ 7 scanf("%d",&n); 8 for (int i = 1 ; i <= n ; ++i) scanf("%d",&a[i]); 9 for (int i = 1 ; i <= n ; ++i){ 10 for (int j = 1 ; j <= 8 ; ++j) cnt[j] = 0; 11 for (int j = 1 ; j <= 8 ; ++j) for (int k = 1 ; k <= n ; ++k) next[i][j][k] = 1e9; 12 for (int j = i ; j <= n ; ++j) next[i][a[j]][++cnt[a[j]]] = j; 13 } 14 for (int num = 0 ; num * 8 <= n ; ++num){ 15 for (int i = 0 ; i < 256 ; ++i) for (int j = 0 ; j <= 8 ; ++j) dp[i][j] = 1e9; 16 dp[0][0] = 1; 17 for (int i = 0 ; i < 256 ; ++ i) for (int j = 0 ; j <= 8 ; ++j) for (int k = 1 ; k <= 8 ; ++k){ 18 if ((ask(k) & i) || dp[i][j] > n) continue; 19 dp[i ^ ask(k)][j] = min(dp[i ^ ask(k)][j],next[dp[i][j]][k][num] + 1); 20 dp[i ^ ask(k)][j + 1] = min(dp[i ^ ask(k)][j + 1],next[dp[i][j]][k][num + 1] + 1); 21 } 22 for (int j = 0 ; j <= 8 ; ++j) if (dp[255][j] <= n + 1) ans = max(ans,8 * num + j); 23 } 24 cout<<ans<<endl; 25 return 0; 26 }