zoukankan      html  css  js  c++  java
  • 1067A

    题目大意

      有一串长度为$n$的数字,其中由一些数字看不清了($-1$的数字就是看不清的),而这些数字取值范围满足$0leq a leq20 $,问满足一下条件

    $(1) a_{1}leq a_{2}$

    $(2) a_{n}leq a_{n-1}$

    $(3) a_{i}leq max(a_{i-1},a_{i+1}) iin [2,n-1]$

    的方案数有多少种.

    分析

      dp。$dp[i][num][flag]=$第$i$个数字为$num$,$(1) flag=0$时,表示$a_{i-1}< a_{i}$,$(2) flag=1$时,表示$a_{i-1}geq a_{i}$的方案数。可能有人会有点疑问,为什么这样定义,看下面几张图

    根据条件,连续的3个点是互相关联的,如果我们将这三个连续的写为$A,B,C$如果想让$C$合法,由上面的图上我们得到由$Bgeq C且Ageq C$和$C>B$这样两种情况得到,然后再根据$flag$的定义,我们可以得到以下状态转移方程$dp[i][num][0]=sum_{j=1}^{num-1} (dp[i-1][j][1]+dp[i-1][j][0])$和$dp[i][num][1]=dp[i][num][0]+sum_{j=num}^{200} {dp[i-1][j][1]}$,这里$num$的两种情况的转移方程并不差别。但是如果这样做复杂度是$O_{(ncdot num cdot num)}$我们可以通过前缀和和后缀和,将复杂度变成$O_{(ncdot num)}$详细看代码

    #define frp
    
    #include<bits/stdc++.h>
    #include <algorithm>
    #include <cmath>
    #include <iostream>
    #include <cstring>
    #include <string>
    #include <string.h>
    #include <iomanip>
    
    using namespace std;
    typedef long long ll;
    const ll INF = 0x3f3f3f3f;
    const ll inf = 0x7fffff;
    const int maxn = 2e6;
    const int MAXN = 1100000 + 10;
    const int MOD = 1e9 + 7;
    const ll mod = 998244353; 
    ll dp[100005][210][2];
    ll a[maxn];
    int n;
    
    void solve() {
        cin >> n;
        for (int i = 0; i < n; ++i) {
            cin >> a[i + 1];
        }
        if (a[1] != -1) {
            dp[1][a[1]][0] = 1;
        } else {
            for (int i = 1; i < 201; ++i) {
                dp[1][i][0] = 1;
            }
        }
        for (int i = 2; i < n + 1; ++i) {
            if (a[i] == -1) {//如果是看不清的数字
                ll sum = 0;
                for (int j = 1; j < 201; ++j) {//a[i-1]<a[i]的情况,因为1是不可能比其他数字大的,所以是求前缀和
                    dp[i][j][0] = sum;
                    sum = (sum + dp[i - 1][j][0] + dp[i - 1][j][1]) % mod;
                }
                sum = 0;
                for (int j = 200; j > 0; --j) {//从后往前的原因是a[i-1]>=a[i],a[i-2]必须大于a[i-1],则a[i-2]必定大于a[i],如果从前往后,那么a[i-2]是不一定大于a[i]的,有点像01背包二维变一维时的思路
                    sum = (sum + dp[i - 1][j][1]) % mod;//后缀和
                    dp[i][j][1] = (sum + dp[i - 1][j][0]) % mod;
                }
            } else {
                for (int j = 1; j < a[i]; ++j) {
                    dp[i][a[i]][0] = (dp[i][a[i]][0] + dp[i - 1][j][0] + dp[i - 1][j][1]) % mod;
                }
                for (int j = 200; j >= a[i]; --j) {
                    dp[i][a[i]][1] = (dp[i][a[i]][1] + dp[i - 1][j][1]) % mod;
                }
                //这条不要忘了
                dp[i][a[i]][1] = (dp[i][a[i]][1] + dp[i - 1][a[i]][0]) % mod;
            }
        }
        if (a[n] != -1) {//如果第n个是看得清的数字,直接输出
            cout << dp[n][a[n]][1] << endl;
        } else {//如果是看的不清的数字,将1-200之间可能的方案数相加,即为所求的答案
            ll ans = 0;
            for (int i = 1; i < 201; ++i) {
                ans = (ans + dp[n][i][1]) % mod;
            }
            cout << ans << endl;
        }
    }
    
    int main() {
        ios_base::sync_with_stdio(0);
        cin.tie(0);
        cout.tie(0);
    #ifdef frp
        freopen("D:\coding\c_coding\in.txt", "r", stdin);
    //    freopen("D:\coding\c_coding\out.txt", "w", stdout);
    #endif
        int t = 1;
        cin >> t;
        while (t--) {
            solve();
        }
        return 0;
    }
    

      

  • 相关阅读:
    【转】卡特兰数四个公式(简单)
    【基础算法-ST表】入门 -C++
    【题解】[Nwerc 2006]escape -C++
    C#高级编程第11版
    C#高级编程第11版
    C#高级编程第11版
    C#高级编程第11版
    C#高级编程第11版
    109th LeetCode Weekly Contest Knight Dialer
    109th LeetCode Weekly Contest Number of Recent Calls
  • 原文地址:https://www.cnblogs.com/visualVK/p/9942316.html
Copyright © 2011-2022 走看看