|LIS| = 3(最長上升子序列,DP)
阿新 • • 發佈:2022-03-15
題意
求滿足下列條件的序列個數:
- 長度為\(n\)
- 序列的每個元素值都在\([1,m]\)
- 最長嚴格上升子序列的長度恰好為\(3\)
資料範圍
\(3 \leq n \leq 1000\)
\(3 \leq m \leq 10\)
思路
首先回顧一下最長上升子序列的做法:
- 維護一個vector,記為\(L\)
- 對於每個元素\(A_i\),找到滿足\(L_j \geq A_i\)的最小元素的下標(二分)。如果存在的話,用\(A_i\)替換\(L_j\)。否則,將\(A_i\)新增到\(L\)的後面。
- 最終\(L\)的長度。
因為我們只關心長度小於等於\(3\)的最長上升子序列。因此考慮\(f_{i, a, b, c}\)
轉移的過程中,列舉第\(i\)個元素的數值,記為\(x\)。若\(1 \leq x \leq a\),那麼\(x\)可以替換\(a\),則\(f_{i, x, b, c} = f_{i, x, b, c} + f_{i - 1, a, b, c}\);若\(a + 1 \leq x \leq b\),那麼\(x\)可以替換\(b\),則\(f_{i, a, x, c} = f_{i, a, x, c} + f_{i - 1, a, b, c}\);若\(b + 1 \leq x \leq c\)
這裡需要注意一點,就是若\(a = m + 1\),表示長度為\(1\)的最長上升子序列不存在。\(b, c = m + 1\)同理。
程式碼
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int mod = 998244353, N = 1010, M = 15; int n, m; ll f[N][M][M][M]; int main() { scanf("%d%d", &n, &m); f[0][m + 1][m + 1][m + 1] = 1; for(int i = 1; i <= n; i ++) { for(int a = 1; a <= m + 1; a ++) { for(int b = 1; b <= m + 1; b ++) { for(int c = 1; c <= m + 1; c ++) { for(int x = 1; x <= a && x <= m; x ++) { f[i][x][b][c] = (f[i][x][b][c] + f[i - 1][a][b][c]) % mod; } for(int x = a + 1; x <= b && x <= m; x ++) { f[i][a][x][c] = (f[i][a][x][c] + f[i - 1][a][b][c]) % mod; } for(int x = b + 1; x <= c && x <= m; x ++) { f[i][a][b][x] = (f[i][a][b][x] + f[i - 1][a][b][c]) % mod; } } } } } ll ans = 0; for(int i = 1; i <= m; i ++) { for(int j = 1; j <= m; j ++) { for(int k = 1; k <= m; k ++) { ans = (ans + f[n][i][j][k]) % mod; } } } printf("%lld\n", ans); return 0; }