HDU--4055(DP*)
2015-06-09 20:35:57
题目:题意比较简单,有 n+1 个数组成若干排列,给出 n 个字符,每个字符为 ‘I' / ’D‘ / ’?',分别表示排列中前一个数到后一个数是 上升/下降/上升或下降 的关系。问在 n+1 个数的所有排列中有多少个排列符合条件。
思路:这题虽然在现场过了很多支队伍(70左右),但是自己模拟的时候硬是想不出来... 太弱了。
dp 是听明显的,状态定义也很自然,dp[i][j] 表示 i 个数的排列结尾是 j 且满足条件的排列数。
如果遇到 'I',dp[i][j] = dp[i - 1][1] + dp[i - 1][2] + .... + dp[i][j - 1]
如果遇到 'D' dp[i][j] = dp[i - 1][j] + dp[i - 1][j + 1] + ... + dp[i - 1][i - 1]
如果是 '?' 那么就是前两式的和
第一条比较好理解,在 i - 1 长度里找结尾比 j 小的即可。考虑第二条,我需要在排列中加入 i 这个数,且使得结尾是 j,我们可以把 i - 1 个数排列
中 >= j 的数全部 +1,这样就会剩下 j 一个数没有加,放在最后面即可。(这个思路是不断加入数,巧妙利用了 dp 的递推性,第二条转化方法值得思考)
※具体实现中我把 dp 定义为了前缀和,为了方便。
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const ll mod = 1000000007; char s[1010]; ll dp[1010][1010]; int main(){ while(scanf("%s",s + 1) != EOF){ int len = strlen(s + 1); for(int i = 1; i <= len + 1; ++i) dp[1][i] = 1; for(int i = 2; i <= len + 1; ++i){ if(s[i - 1] == 'I'){ for(int j = 1; j <= i; ++j) dp[i][j] = (dp[i][j - 1] + dp[i - 1][j - 1]) % mod; } else if(s[i - 1] == 'D'){ for(int j = 1; j <= i; ++j) dp[i][j] = (dp[i][j - 1] + dp[i - 1][i - 1] - dp[i - 1][j - 1] + mod) % mod; } else{ for(int j = 1; j <= i; ++j) dp[i][j] = (dp[i][j - 1] + dp[i - 1][i - 1]) % mod; } } printf("%I64d\n",dp[len + 1][len + 1]); } return 0; }