1. 程式人生 > >Codeforces Round #384 (Div. 2) E. Vladik and cards 狀壓dp

Codeforces Round #384 (Div. 2) E. Vladik and cards 狀壓dp

答案 clu test none urn 數字 ble inline return

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 }
Codeforces Round #384 (Div. 2)E

Codeforces Round #384 (Div. 2) E. Vladik and cards 狀壓dp