题意
给定一个字符序列,要求求对应地合法数字序列的排列数目,满足对应位置的数字的大小关系和字符序列匹配
II 对应 1 2 3
ID 对应 1 3 2 或者 2 3 1
I 表示递增,D表示递减,?表示随意。
题解
dp[i]是长度为i的排列数,那么考虑大小关系,显然状态转移需要第i-1位的数的值
dp[i][j]表示长度为i,末尾数字为j的合法排列数。
这道题有一个障碍,dp[i][]排列的是1-i这i个数,dp[i+1][]排列的是1-i+1这些数,dp[i+1][j],只要j!=i+1,就意味着前i个数不是1-i了,插人了一个i+1,似乎违反了dp无后效性的原则,状态转移不合法。
事实上,这道题的排列的合法与否,与数字本身是没有关系的,只与数字的相对大小关系有关,所以我们应该这样定义dp[i],i个大小不等的数字的合法排列数,dp[i][j]则是i个数字中第j小的数结尾的合法排列数(好绕啊)
转移方程
I dp[i][j]=sum[i-1][j-1];
D dp[i][j]=sum[i-1][i-1]-sum[i-1][j-1]
? dp[i][j]=sum[i-1][i-1]
sum[i][j]=sigma(dp[i][k],1<=k<=j)
这里有个很微妙的问题,dp[i][j]表示1-i中j来结尾,那么dp[i-1][j]实际上是j+1在结尾,这里要有dp[i][j]是把i插人了i-1序列中并把j给提到了最后位置的想法
(这诡异的题目不知道自己过两个月还能不能看懂……
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=1000;
const int h=1e9+7;
LL dp[maxn+5][maxn+5];
LL sum[maxn+5][maxn+5];
char s[maxn+5];
int main(void)
{
#ifdef ex
freopen ("in.txt","r",stdin);
#endif
while (scanf("%s",s)!=EOF)
{
memset(dp,0,sizeof(dp));
memset(sum,0,sizeof(sum));
int len=strlen(s);
dp[1][1]=1;
sum[1][1]=1;
for (int i=2;i<=len+1;++i)
{
if (s[i-2]=='I')
{
for (int j=1;j<=i;++j)
{
dp[i][j]=sum[i-1][j-1];
}
}
else if (s[i-2]=='D')
{
for (int j=1;j<=i;++j)
{
dp[i][j]=((sum[i-1][i-1]-sum[i-1][j-1])%h+h)%h;
}
}
else
{
for (int j=1;j<=i;++j)
{
dp[i][j]=sum[i-1][i-1];
}
}
sum[i][1]=dp[i][1];
for (int j=2;j<=i;++j)
{
sum[i][j]=(sum[i][j-1]+dp[i][j])%h;
}
}
LL ans=sum[len+1][len+1]%h;
printf("%I64d\n",ans);
}
}