HDU 4055:Number String(DP计数)

http://acm.hdu.edu.cn/showproblem.php?pid=4055

题意:给一个仅包含‘I','D','?'的字符串,’I'表示前面的数字比后面的数字要小(Increase升序),'D'表示前面的数字比前面的数字要大(Decrease降序),'?'表示有可能是'I'也有可能是'D',长度为n的字符串就有n+1个数字,在这n+1个数字里面,问符合给出的字符串的条件的序列数有多少种。

思路:dp[i][j]的定义:i表示当前枚举到第i个字符(即长度为i+1的时候的序列),j表示当前序列长度为i+1的时候第i+1位插入的数是什么,dp[i][j]表示长度为i+1的序列末尾为j的序列数总共有多少种。

首先需要转化一个思维:即例如前面序列长度为3,假设s[4] = 'D', 那么加入当前的数的时候,即(3, 1, 2),现在枚举的是dp[4][1],即要插入1,那么可以当成前面大于等于1(j)的数全部+1,序列就变成(4, 2, 3, 1),因此可以用前面的来推出后面的。

当s[i]为'I'的时候,插入的数要比前面一位大,因此前面一位是(1~j-1)的时候都可以插入,所以dp[i][j] = dp[i-1][j-1] + ... + dp[i-1][1].

当s[i]为'D'的时候,插入的数要比前面一位小,前面说了,要插入的数j在前面重复的时候,可以把前面的大于等于j的数都+1,所以前面是(j~i)都可以插入(j也是可取的,因为+1就变成j+1了),dp[i][j] = dp[i-1][j] + ... + dp[i-1][i].

这个式子是可以处理出前缀和的,即每次都把dp[i][j] = dp[i][j-1]这样就可以累加起来了。

而且当前的i只和i-1有关,可以使用滚动数组,还有交C++如果MOD运算过多会超时。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4 #define N 1010
 5 const int MOD = 1e9 + 7;
 6 LL dp[2][N];
 7 char s[N];
 8 int main() {
 9     while(~scanf("%s", s + 1)) {
10         int n = strlen(s + 1);
11         memset(dp, 0, sizeof(dp));
12         int pre = 0, now = 1;
13         dp[pre][1] = 1;
14         for(int i = 1; i <= n; i++) {
15             for(int j = 1; j <= i + 1; j++) {
16                 dp[now][j] = dp[now][j-1]; // 前缀和
17                 if(s[i] != 'I') dp[now][j] = (dp[now][j] + (dp[pre][i] - dp[pre][j-1]) + MOD) % MOD;
18                 if(s[i] != 'D') dp[now][j] = (dp[now][j] + dp[pre][j-1]) % MOD;
19             }
20             pre ^= now; now ^= pre; pre ^= now;
21         }
22         printf("%lld
", dp[n%2][n+1]);
23     }
24     return 0;
25 }
原文地址:https://www.cnblogs.com/fightfordream/p/6830273.html