1. 程式人生 > >Codeforces 1067A - Array Without Local Maximums 計數dp+詳細推導 (Codeforces Round #518 (Div. 1))

Codeforces 1067A - Array Without Local Maximums 計數dp+詳細推導 (Codeforces Round #518 (Div. 1))

CF: *2000  比起同難度級別的題,dp可真的難做,可能是我做dp的題太少了吧。。。。

 

題意:

給定一個長度為n(1~ 1e5)的序列a[],其中a[i] 應該在1-200中間,但是有的值看不到了,用-1表示,

但是知道的是對於 1 < i && i < n  這樣的i :a[i] <= max(a[i-1], a[i+1]) ,並且 a[1] <= a[2],   a[n-1] >= a[n];

問這些不知道的數的所有的可能種數

 

思路:

顯然是dp,狀態就是每個數的值吧??

稍微做過點dp,直接想到的是 dp[i][j] 表示前i個數考慮完了,第i個數 數值為j時的所有種類數,但是考慮到狀態轉移(也就是i和i-1位置的種數關係),我們還要考慮一維表示當前數跟前一個數的大小關係,看似有三種關係:大於,等於,小於

其實:我們按照上述dp[i][j] 考慮的時候,輸出的肯定是dp[n][] 這樣的,因為規定了 a[n] <= a[n-1];所以我們可以把 <= 看作一個狀態; 

所以有:dp[i][j][k] ,k=1時: 前i個數已經考慮完,第i個數值為 j,且第i個數>第i-1個數 時候的所有種類數;;;;;;;;;;dp[i][j][k] ,k=0時: 前i個數已經考慮完,第i個數值為 j,且第i個數<=第i-1個數 時候的所有種類數;;;;;;;;;

 

對於第i個數,我們分兩種情況討論:

如果有確定值的話,假設值為x,那隻要考慮dp[i][x][]就好;  求dp[i][x][1] 時:就是把前i-1箇中小於x的都加上,因為此時第i個數大於第i-1個數(方程式中第二維,我們要遍歷的那個數),所以 dp[i-1][t][0] 和 dp[i-1][t][1]都要加上;

而在求dp[i][x][0] 時:對於t>x,加上dp[i-1][t][0] 因為 此時第i個數小於第i-1個數,;對於t=x,加上dp[i-1][t][0]+dp[i-1][t][1];

如果此時是-1的話,那我們的第2維 j 就要考慮等於1~200的所有情況,然而按照上述求某一個數的方式複雜度看似會上去,但我們運用字首和,字尾和的想法,遍歷的時候可以把前面所有的情況算進去;

 

 

 

 

#include<bits/stdc++.h>

using namespace std;

#define out fflush(stdout);
#define fast ios::sync_with_stdio(0),cin.tie(0);

#define FI first
#define SE second

typedef long long ll;
typedef pair<ll,ll> P;

const int maxn = 1e5 + 7;
const int INF = 0x3f3f3f3f;
const ll mod = 998244353;


int n;
ll a[maxn];
ll dp[maxn][200+7][2] = {0};
// dp[i][j][k] 表示前i個數,第i個數為j時,跟前一個數大小關係為k時的最優解;k=1:>左邊; k=0:<=左邊


int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) {
        scanf("%lld", &a[i]);
    }

    if(a[1] == -1) {
        for(int j = 1; j <= 200; ++j) {
            dp[1][j][1] = 1;
        }
    }
    else {
        dp[1][a[1]][1] = 1;
    }
    
    for(int i = 2; i <= n; ++i) {
        if(a[i] == -1) {
            ll cur = 0;
            for(int j = 1; j <= 200; ++j) {
                dp[i][j][1] = cur;
                cur = (cur + (dp[i-1][j][1] + dp[i-1][j][0])) % mod;
            }
            cur = 0;
            for(int j = 200; j >= 1; --j) {
                cur = (cur + (dp[i-1][j][0])) % mod;
                dp[i][j][0] = (dp[i-1][j][1] + cur) % mod;
            }
        }
        else {
            for(int j = 1; j < a[i]; ++j) {
                dp[i][a[i]][1] = (dp[i][a[i]][1] + (dp[i-1][j][1] + dp[i-1][j][0])) % mod;
            }

            for(int j = 200; j >= a[i]; --j) {
                dp[i][a[i]][0] = (dp[i][a[i]][0] + dp[i-1][j][0]) % mod;;
            }
            dp[i][a[i]][0] = (dp[i][a[i]][0] + dp[i-1][a[i]][1]) % mod;
        }
    }
    ll ans = 0;
    if(a[n] == -1) {
        for(int i = 1; i <= 200; ++i) {
            ans = (ans + dp[n][i][0]) % mod;
        }
    }
    else {
        ans = dp[n][a[n]][0];
    }

    printf("%lld\n", ans);
    return 0;
}