题意:给出一个字符串,问1~n这n个数中有多少种排列满足给定字符串的要求,字符串str[i]=='I'代表第i位数字要比它前一位大,str[i]=='D'代表第i位数字要比它前一位小,str[i]=='?'代表第i位数字任意。
思路:刚开始没想出怎么做,看了看题解,感觉大体思路还是能看懂的,但是细节的地方怎么也没看明白,我理解能力是有多差Orz……后来没办法了,自己YY过了,果然还是自己想的思路好写。我的思路是这样的:在1~n中拿走任意一个数,剩下的数其实可以重新编号(1~n-1),所以每一次选的时候也可以看成是一个1~k(k是当前剩下的数)的数选一个。这样的话,第i次选的数就取决于第i-1次选的数在它剩下的数中的位置了。用dp[i][j]表示到第i位时,在剩下的数中选第j个数的排列的个数。这样的话状态转移方程很容易就能列出来:如果str[i]=='I',dp[i][j]=sum{dp[i-1][1]~dp[i-1][j]},如果str[i]=='D',dp[i][j]=sum{dp[i-1][j+1]~dp[i-1][k]}(k是到i-1位时剩余的数)。这题数组开long long 的话居然会超时,这数据……不能多说。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=1000+10;
const int mod=1000000007;
char str[maxn];
int dp[maxn][maxn],sum[maxn][maxn];
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int n;
while(~scanf("%s",str))
{
n=strlen(str);
n++;
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;++i) sum[i][0]=0;
for(int i=1;i<=n;++i)
{
dp[1][i]=1;
sum[1][i]=i;
}
for(int i=2;i<=n;++i)
{
for(int j=1;j<=n-i+1;++j)
{
if(str[i-2]=='I'||str[i-2]=='?')
dp[i][j]+=sum[i-1][j]-sum[i-1][0];
if(str[i-2]=='D'||str[i-2]=='?')
dp[i][j]+=sum[i-1][n-i+2]-sum[i-1][j];
dp[i][j]%=mod;
sum[i][j]=(sum[i][j-1]+dp[i][j])%mod;
}
}
int res=(dp[n][1]+mod)%mod;
printf("%d\n",res);
}
return 0;
}