题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4689
题目大意:
给出第 i 位置上的数比 i 大还是小。
求可能的方法数。
算法:
这个DP有点儿绕。
由左向右依次处理,注意只能把已经处理的数填在已经处理的位置上。也就是处理到 i 时只能把数字1 ~ i 填到第1 ~ i 的位置上。
处理到第 i 位时,如果要把数字 i 填到前面去,那么就填,否则不处理。
第 i 位的符号如果是 - ,就从前面找个数填上,如果是 + 先不处理。
d[i][j]表示的方法数是,
前 i 位的 - 号都填上之后,有 j 个 + 号没填,
等价于 [1, i] 区间与 [i + 1, n] 区间交换了 j 个数,
等价于数字1 ~ i中有 j 个数字没有填在区间 [1, i] 里。
等价于位置区间 [1, i] 中有 j 个位置填写的是大于 i 的数,也就是数字 1 ~ i 填完之后前 i 个位置还有 j 个空位。
对于每个位置,都有把数字 i 填到位置 i 前面或者后面两种选择。
具体转移如下:
如果第 i 位是 + ,那么
d[i][j] += d[i - 1][j - 1];
把数字 i 填到后面。又因为第 i 个位置因为是 + ,现在没法填,先空着。于是什么也不做。空位增加了1。
d[i][j] += d[i - 1][j] * j;
把数字 i 填到前面。那么从前面 j 个空着的位置中找一个把数字 i 填进去。第 i 个位置还是不动。
如果第 i 位是 - ,那么
d[i][j] += d[i - 1][j] * j;
把数字 i 填到后面。从数字 1 ~ i 尚未使用的 j 个中挑一个填在第 i 位。这样空位还是 j 个。
d[i][j] += d[i - 1][j + 1] * (j + 1) * (j + 1)
把数字 i 填到前面。从数字 1 ~ i尚未使用的 j + 1 个中挑一个填在第 i 位。在位置1 ~ i 中的 j + 1 个空位中找一个把数字 i 填进去。这样空位也还是 j 个。
代码:
#include <cstdio> #include <iostream> #include <algorithm> #include <sstream> #include <cstdlib> #include <cstring> #include <string> #include <climits> #include <cmath> #include <queue> #include <vector> #include <stack> #include <set> #include <map> #define INF 0x3f3f3f3f #define eps 1e-8 using namespace std; const int MAXN = 50; char s[MAXN]; long long d[MAXN][MAXN]; int main() { while(scanf("%s", s) == 1) { memset(d, 0, sizeof(d)); if(s[0] == '-') { puts("0"); continue; } int n = strlen(s); d[1][1] = 1; for(int i = 2; i <= n; i ++) { for(int j = 0; j <= i; j ++) { if(s[i - 1] == '+') { if(j) { d[i][j] += d[i - 1][j - 1]; } d[i][j] += d[i - 1][j] * j; } else { d[i][j] += d[i - 1][j] * j; d[i][j] += d[i - 1][j + 1] * (j + 1) * (j + 1); } } } printf("%I64d\n", d[n][0]); } return 0; }