题目描述
样例
Input
19198210
Output
5
算法1
(类似状态机的dp)
这题其实不难,仔细思考片刻这题其实就可以把他分成多个部分:
将题目中的2 3 4 5 6 7 部分我们分为2 3 4 5 6 7阶段
上升部分:就是 < ,以及 < 两个部分, 也就是题中2和4 下降部分:也就是其他部分,题中3 5 6 7 四个部分 分成这两个部分以后,首先我们看看2阶段的状态转移:
若只看1,2阶段,我们的任务便变成了,寻找长度为2的上升子序列 若当前想要得到以第 i 个字符结尾的长度为2的上升子序列
那么我们要的是前 i - 1 个字符中所有小于第 i 个字符和 当我们求出第2阶段值后,类似的第3阶段我们的任务是: 寻找长度为3并先上升后下降的子序列: 我们已经求出上升并长度为2的子序列 所以我们接下来需要,求得是以第i个字符结尾长度为3的先上升再下降的子序列:
我们要的是前i - 1个字符为结尾的长度为2的上升序列中结尾字符 大于 第 i 个字符的和 以此类推第 4 5 6 7 阶段便是从前一个阶段往后加一个字符
C++ 代码
/*
记得取模!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
#include "bits/stdc++.h"
using namespace std;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef long long ll;
ll num[10];// 用来记录大小为 0 1 2 ...... 8 9 结尾的前一个阶段的前缀和
ll dp[10][N];
int main() {
string s;
cin >> s;
for (int i = 0; i < s.size(); i++)
dp[1][i] = 1;
for (int i = 2; i <= 7; i++) {
memset(num, 0, sizeof num);
if (i == 2 || i == 4) {
for (int j = 0; j < s.size(); j++) {
ll x = s[j] - '0', res = 0;
for (int k = 0; k < x; k++)
res = (num[k] + res) % mod;
dp[i][j] = res;
num[x] = (dp[i - 1][j] + num[x]) % mod;
}
} else {
for (int j = 0; j < s.size(); j++) {
ll x = s[j] - '0', res = 0;
for (int k = 9; k > x; k--)
res = (num[k] + res) % mod;
dp[i][j] = res;
num[x] = (dp[i - 1][j] + num[x]) % mod;
}
}
}
ll ans = 0;
for (int i = 0; i < s.size(); i++)
ans = (dp[7][i] + ans) % mod;
cout << ans << endl;
}
总结
本题所用姿势点
状态机dp
遇到的bugs
忘记取模