hdu 4055 Number String
题意:
给出一个长度为n-1的字符串,字符串包含了'I','D','?',现在需要你按照字符串的要求构造排列,求方案数对1e9+7取模('I'的要求是前一个数要小于当前的数,'D'的要求是前一个数要大于当前的数,'?'的要求是前一个数随便放)
题解:
求方案数,首先会想到排列组合,然后会想到递推,这题很显然不能使用排列组合,考虑递推。
现在的问题是定义状态和状态转移:
首先考虑对于答案来定义状态,定义dp[i]表示1~i排列符合条件的数量,那么答案就是dp[n],但是可以发现的是这种状态没法转移因为不能满足当前某个字符的要求,所以应该再加一维dp[i][j]表示1~i的排列中最后一位数是j。
现在考虑转移:
如果当前字符串为'I' 那么dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j - 2] + ...... dp[i - 1][1]
如果当前字符串为'D',但是会发现dp[i - 1][i]根本没有计算过,所以这样的转移不可行,但是我们会发现,当最后一位是j的时候,将1~i-1位置上的大于等于j的数,数值加1,依然会满足条件,那么dp[i][j] = dp[i - 1][j ] + dp[i - 1][j + 1] + ...... dp[i - 1][i -1],
如果当前字符串为'?'那么dp[i][j] = dp[i - 1][1] + dp[i - 1][2] + ...... dp[i - 1][i - 1]
然后前缀优化优化就可以搞了。。。
代码:
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> using namespace std; const int N = 1e3 + 7; const int mod = 1e9 + 7; char ch[N]; int dp[N][N], sum[N]; int main () { scanf ("%s", ch + 1); int n = strlen (ch + 1) + 1; dp[1][1] = 1; for (int i = 2; i <= n; ++i) { for (int j = 1; j < i; ++j) sum[j] = (sum[j - 1] + dp[i - 1][j]) % mod; for (int j = 1; j <= i; ++j) { if (ch[i - 1] == 'I') dp[i][j] = sum[j - 1] % mod; else if (ch[i - 1] == 'D') dp[i][j] = (sum[i - 1] - sum[j - 1] + mod) % mod; else dp[i][j] = sum[i - 1] % mod; } } int ret = 0; for (int i = 1; i <= n; ++i) ret = (ret + dp[n][i]) % mod; cout << ret << endl; return 0; }
总结:
排列计数一般都根据答案来定义,问排列的方案第一维一般是1~i的排列的方案数,第二维第三维根据条件来定义。。。(套路)~