zoj 3543 Number String

zoj  3543 Number String

这道题搞了,一个晚上,别人说是很少的dp,但是就是搞不出来,看来确实是弱爆了……T_T,网上结题报告个人认为讲的都不清楚,对与细节的东西都没有讲明白,这里若菜讲讲我个人的理解……


如果题目要求的序列不是n的一个排列,而是n个数,每个数的范围在【1,n】,求符合给定递增递减的序列有多少个,这样题目就好做了吧?  不用想,状态表示dp[ len ] [ i ]  表示长度为len,末尾的数字为 i 的序列的个数。。。直接O(n^2) 就可以搞出来。。。但是这道题目要求的是1,2,....,n的 全排列,这样做显然是不行的。

 状态表示还是dp[ len ] [ i ]  表示长度为len,末尾的数字为 i 的序列的排列个数,这样 i 的取值范围就是[1, len],当len = len+1 是,i 的取值范围就是[1,len+1],这时如果i取值<=len ,在之前的排列后加上个i,那么肯定是不行的,因为这个序列不是排列(有两个i),但是如果把原来排列i之后的数全部加+1,那么这样就符合条件了,比如原来 序列是: “12” , 现在是在其后加 1 , 那么“12” -> "23" ,然后加1,即是"231"...明白这点就保证“231”是一个合法的排列了,其他的都简单了


PS: 开始我用long long ,T了两次,该处int,1000+ms就过了……

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;

const int mod=1000000007;
typedef long long ll;
char s[1010];
int dp[1020][1020],sum[1020][1020];

int main()
{
    while(scanf("%s",s+2)==1)
    {
        int len=strlen(s+2);
        memset(dp,0,sizeof(dp));
        memset(sum,0,sizeof(sum));
        dp[1][1]=sum[1][1]=1;
        for(int i=2;i<len+2;i++)
        {
            for(int j=1;j<=i;j++)
            {
                if(s[i]=='I'||s[i]=='?')
                   dp[i][j]=sum[i-1][j-1];
                if(s[i]=='D'||s[i]=='?')
                   dp[i][j]=(dp[i][j]+sum[i-1][i-1]-sum[i-1][j-1])%mod;
                sum[i][j]=(sum[i][j-1]+dp[i][j])%mod;
            }
        }
        int ans=0;
        for(int i=1;i<=len+1;i++)
          ans=(ans+dp[len+1][i])%mod;
        printf("%d\n",(ans%mod+mod)%mod);
    }
    return 0;
}


你可能感兴趣的:(zoj 3543 Number String)