zoukankan      html  css  js  c++  java
  • hdu 4055 Number String

    Number String

    http://acm.hdu.edu.cn/showproblem.php?pid=4055

    Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

    Problem Description
    The signature of a permutation is a string that is computed as follows: for each pair of consecutive elements of the permutation, write down the letter 'I' (increasing) if the second element is greater than the first one, otherwise write down the letter 'D' (decreasing). For example, the signature of the permutation {3,1,2,7,4,6,5} is "DIIDID".
    Your task is as follows: You are given a string describing the signature of many possible permutations, find out how many permutations satisfy this signature.
    Note: For any positive integer n, a permutation of n elements is a sequence of length n that contains each of the integers 1 through n exactly once.
     
     
    Input
    Each test case consists of a string of 1 to 1000 characters long, containing only the letters 'I', 'D' or '?', representing a permutation signature.
    Each test case occupies exactly one single line, without leading or trailing spaces.
    Proceed to the end of file. The '?' in these strings can be either 'I' or 'D'.
     
     
    Output
    For each test case, print the number of permutations satisfying the signature on a single line. In case the result is too large, print the remainder modulo 1000000007.
     
    Sample Input
    II
    ID
    DI
    DD
    ?D
    ??
     
    Sample Output
    1
    2
    2
    1
    3
    6
     
    Hint
    Permutation {1,2,3} has signature "II".
    Permutations {1,3,2} and {2,3,1} have signature "ID".
    Permutations {3,1,2} and {2,1,3} have signature "DI".
    Permutation {3,2,1} has signature "DD".
    "?D" can be either "ID" or "DD".
    "??" gives all possible permutations of length 3.
     
     
    题意:
    给出长为n的一个字符串,根据这个字符串构造长为n+1的数字串
    字符为‘I’,下一个数字要比这一个数字大
    字符为‘D’ ,下一个数字要比这一个数字小
    字符为‘?’,下一个数字没有限制
    问数字串有多少种方案
     
    一般套路:
    f[i][j]表示长为i的串中,最后一个数字为j的方案数
    若字符为‘I’,f[i][j]= Σ f[i-1][x]   1 <= x < j     
    若字符为‘D’,f[i][j]= Σ f[i-1][x]  j <= x <= i-1  
    若字符为‘?’,f[i][j]=Σ f[i-1][x]  1 <= x <= i-1
    我们虽然根据 字符保证了相邻两个数字的大小关系,但并没有保证数字串里前i-1个数字没有数字j
    这就有了后效性。怎么办?
    给状态增加含义:必须选前i-1个数字
    我们将过程想象为一个一个填数字的过程
    那么由f[i-1][]向f[i][]的转移,就是在末尾(i位置)填上数字i
    (因为状态的定义是必须选前i-1个数字)
    那么岂不是只能填数字i?字符为‘D’时不就错了吗?第二维不就没有用吗?
    我们考虑第二维j(要填的最后一个数字),
    我们是否可以将填数字i转换到填数字j,
    那么状态转移的时候,就要想如何填j使相邻数字的大小关系不变
    将前i-1个数字>=j的都+1,这样就拿出了j,放在最后面
    所以,上方状态转移成立
    时间复杂度?O(n³) TLE
     
    前缀和优化,时间复杂度O(n²)
    其实很简单,照着原方程x的取值范围写就行
     1 <= x < j     前缀和就是sum[i-1][j-1]
    j <= x <= i-1  前缀和就是 sum[i-1][i-1]-sum[i-1][j-1]
     1 <= x <= i-1  前缀和就是sum[i-1][i-1]
    前缀和的更新: sum[i][j]=(sum[i][j-1]+f[i][j])%mod;
     
    前缀和优化代码
    #include<cstdio>
    #include<cstring>
    #define mod 1000000007
    using namespace std;
    int len,f[1002][1002],sum[1002][1002];
    char s[1001];
    int main()
    {
        while(scanf("%s",s+1)!=EOF)
        {
            memset(f,0,sizeof(f));
            len=strlen(s+1);
            f[1][1]=1; sum[1][1]=1;
            for(int i=2;i<=len+1;i++)
                  for(int j=1;j<=i;j++)
                 {
                     if(s[i-1]=='I') f[i][j]=sum[i-1][j-1];
                     else if(s[i-1]=='D') f[i][j]=((sum[i-1][i-1]-sum[i-1][j-1])%mod+mod)%mod;
                     else f[i][j]=sum[i-1][i-1];
                     sum[i][j]=(sum[i][j-1]+f[i][j])%mod;
                 }
            printf("%d
    ",sum[len+1][len+1]); 
        }
    }

    未优化代码

    #include<cstdio>
    #include<cstring>
    #define mod 1000000007
    using namespace std;
    int len,f[1002][1002];
    char s[1001];
    int main()
    {
        while(scanf("%s",s+1)!=EOF)
        {
            memset(f,0,sizeof(f));
            len=strlen(s+1);
            f[1][1]=1;
            for(int i=2;i<=len+1;i++)
             for(int j=1;j<=i;j++)
             {
                 if(s[i-1]=='I') 
                 for(int k=1;k<j;k++) f[i][j]+=f[i-1][k];
                 else if(s[i-1]=='D')
                 for(int k=j;k<i;k++) f[i][j]+=f[i-1][k];
                 else
                 for(int k=1;k<i;k++) f[i][j]+=f[i-1][k];
             }
            int ans=0;
            for(int i=1;i<=len+1;i++) ans+=f[len+1][i];
            printf("%d
    ",ans); 
        }
    }
  • 相关阅读:
    linux free
    uptime
    简述负载均衡&CDN技术(转)
    大胆地去做自己坚信的事情,去做不伤害国家和客户的事情 做企业一定要专注。为企业制定战略目标,绝对不能超过三个。超过三个,你就记不住了,员工也记不住
    同一路由器不同vlan之间的通信(一)
    计算机基础之计算机网络与安全
    LayoutInflater的使用
    插入排序
    Java NIO与IO
    高速排序算法
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/6753325.html
Copyright © 2011-2022 走看看