[HDU4055] Number String
题意
给你一个长度为N-1的字符串,第i个字母表示第i个数和第i+1个数的大小关系(I表示递增,D表示递减,?表示未知)问有多少个N排列满足该字符串的构造?
N<=1001
题解
明显是计数dp,思考转移方程式应该怎么推。
设dp[i][j]表示已经填好了i个数的排列且最后一位是j的方案数,注意是i的全排列。
如果第i位字符为I,则转移方程式为:
dp[i][j]=dp[i-1][j-1]+dp[i-1][j-2]+.......+dp[i-1][1]
如果第i位字符为D,则转移方程式为:
dp[i][j]=dp[i-1][j+1]+dp[i-1][j+2]+.......+dp[i-1][i]
这里会出现一个问题,如果在之前已经用过j这个数了呢?
例如一个N=5的全排列{3,2,4,1,5},我们要在后面放一个3,其实我们可以把所有大于等于3的数全部加1,再放入3,该排列就变成了{4,2,5,1,6,3}
这样不仅符合条件,且保证转化后的排列一定不会重复。(因为之前的排列计数是全排列)
然后用前缀和优化一下就可以啦,注意要维护到dp[i][i+1]而不是dp[i][i]
代码
#include<iostream>
#include<cmath>
#include<queue>
#include<vector>
#include<cstring>
#include<algorithm>
#include<map>
#include<set>
#include<stack>
#include<sstream>
#include<string>
#define inf 382890
#define mod 1000000007
using namespace std;
typedef long long ll;
char op[1010];
ll dp[1010][1010];
int main()
{
while(scanf("%s",op)!=-1)
{
int len=strlen(op);
dp[0][1]=1;//只有一个1
for(int i=1;i<=len;i++)
{
for(int j=1;j<=i+1;j++)
{
dp[i][j]=dp[i][j-1];
if(op[i-1]!='I')
{
dp[i][j]+=dp[i-1][i]-dp[i-1][j-1]+mod;
}
if(op[i-1]!='D')
{
dp[i][j]+=dp[i-1][j-1];
}
dp[i][j]%=mod;
}
}
printf("%lld\n",dp[len][len+1]);
}
return 0;
}

浙公网安备 33010602011771号