zoukankan      html  css  js  c++  java
  • hdu 4005 Number String 数列上的DP 【From:2011 Asia Dalian Regional Contest】&& 【SGU489】 Extremal Permutations

    题意:一个1~n的序列,分别用'I','D'表示序列的升降,如{1,2,4,3},则是属于IID的情况,而1~4中属于IID情况的可以有很多不同的序列,给出序列的I、D表现形式(‘?’表示‘I’或‘D’都可以),求出满足这样要求的有多少种。

    例如:

    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.
     

    可以用dp[i][j]表示:处理完第i位,序列末尾位j的序列共有多少个。最后的结果为sigma{dp[N][i]},1≤i≤N

    处理dp[1~i][]的过程中i是依次1~n相加。处理完dp[i-1][]后,加入的数即为i,而dp[i][j]是要将i放进去j换出来,而这里有一种将i放进去j换出来,同时不影响升降顺序的方法是:

    将dp[i-1][j]的i-1个数的序列中 ≥j 的数都加1,这样i-1变成了i,j变成了j+1,而j自然就补在后面了。

    所以对”ID“序列依次处理即可,初始条件:dp[1][1] = 1; 即只有{1}。

    处理‘I’:dp[i][j] = sigma{dp[i-1][x]},其中1≤x≤j-1,可进一步简化,dp[i][j] = dp[i-1][j-1]+dp[i-1][j-1]

    处理‘D’:dp[i][j] = sigma{dp[i-1][x]},其中j≤x≤i-1,可进一步简化,dp[i][j] = dp[i-1][j+1]+dp[i-1][j]

    处理‘?’:dp[i][j] = sigma{dp[i-1][x]},其中1≤x≤i-1,直接计算

    处理过程中在注意边界和取模问题就可以了,附上代码

    View Code
     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstdlib>
     4 #include <cstring>
     5 #include <algorithm>
     6 #include <cmath>
     7 #include <string>
     8 #include <vector>
     9 #include <queue>
    10 #include <set>
    11 #include <map>
    12 #define MAXN 1010
    13 using namespace std;
    14 const int mod = 1000000007;
    15 char str[MAXN];
    16 
    17 int dp[MAXN][MAXN];
    18 int main(){
    19     int N;
    20     char ch;
    21     while(~scanf("%s",str)){
    22         N=strlen(str)+1;
    23         memset(dp,0,sizeof(dp));
    24         dp[1][1]=1;
    25         for(int i=2;i<=N;i++){
    26             ch = str[i-2];
    27             if(ch=='?'){
    28                 int sum=0;
    29                 for(int j=1;j<=i-1;j++)sum = (sum+dp[i-1][j])%mod;
    30                 for(int j=1;j<=i;j++){
    31                     dp[i][j]=sum;
    32                 }
    33             }
    34             else if(ch=='I'){
    35                 for(int j=2;j<=i;j++){
    36                     dp[i][j]=(dp[i][j-1]+dp[i-1][j-1])%mod;
    37                 }
    38             }
    39             else{
    40                 for(int j=i-1;j>=1;j--){
    41                     dp[i][j]=(dp[i][j+1]+dp[i-1][j])%mod;
    42                 }
    43             }
    44         }
    45         int res=0;
    46         for(int i=1;i<=N;i++){
    47             res = (res+dp[N][i])%mod;
    48         }
    49         printf("%d\n",res);
    50     }
    51     return 0;
    52 }

    另外一道题SGU489,求法和上道题类似~~【该死去年过的大连赛区的题,今年做这题居然想不起来】

    题意:给出一个n,求1~n的排列中,满足IDIDID……或DIDIDID……的序列有多少个。

    dp[i][j][0]:处理完第i个数后最后一位数是j,且倒数第二位至最后一位的趋势是下降;

    dp[i][j][1]:处理完第i个数后最后一位数是j,且倒数第二位至最后一位的趋势是上升。

     转移方程:

    dp[i][j][0] = sigma{dp[i-1][x][1]},其中j≤x≤i-1,可进一步简化,dp[i][j][0] = dp[i-1][j+1][0]+dp[i-1][j][1]

    dp[i][j][1] = sigma{dp[i-1][x][0]},其中1≤x≤j-1,可进一步简化,dp[i][j][1] = dp[i-1][j-1][1]+dp[i-1][j-1][0]

    最后求sigma{dp[N][i][0]+dp[N][i][1]},1≤i≤N。注意初始条件为dp[1][1][0] = dp[1][1][1] = 1;所以n==1时要特判一下。当然还需要用到滚动数组优化一下空间,上一题其实也可以用滚动数组,不过范围不大也没必要,代码如下:

    Time: 953 MS

    View Code
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<algorithm>
     5 #include<cstring>
     6 using namespace std;
     7 
     8 int dp[2][10005][2];
     9 int main(){
    10     int n, m, i, j;
    11     int ans;
    12     while(~scanf("%d%d",&n,&m)){
    13         if(n==1){
    14             printf("%d\n",n%m);continue;
    15         }
    16         memset(dp,0,sizeof(dp));
    17         dp[1][1][0] = dp[1][1][1] = 1;
    18         for(i=2;i<=n;i++){
    19             for(j=i;j>=1;j--){
    20                 dp[i&1][j][0] = (dp[i&1][j+1][0]+dp[(i-1)&1][j][1])%m;
    21             }
    22             for(j=1;j<=i;j++){
    23                 dp[i&1][j][1] = (dp[i&1][j-1][1]+dp[(i-1)&1][j-1][0])%m;
    24             }
    25         }
    26         ans = 0;
    27         for(j=1;j<=n;j++){
    28             ans = (ans+(dp[n&1][j][0]+dp[n&1][j][1])%m)%m;//这里很坑爹,一定要两个相加后取模,不然就暴int了
    29 //当然如果直接用long long就不会了,不过dp数组用long long的超空间问题不能实现,那么三个连加就不知道什么情况了。
    30         }
    31         printf("%d\n",ans%m);
    32     }
    33     return 0;
    34 }

    还有一个优化版本,利用的性质是对称性:dp[i][j][0] = dp[i][i-j][1]。

    最后求sigma{dp[N][i][0]+dp[N][i][1]},1≤i≤N。也就是sigma{dp[N][i][0]}*2了,所以可以少计算dp[i][j][0]。

    Time: 500 MS

    View Code
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<algorithm>
     5 #include<cstring>
     6 using namespace std;
     7 
     8 int dp[2][10005];
     9 int main(){
    10     int n, m, i, j;
    11     int ans;
    12     while(~scanf("%d%d",&n,&m)){
    13         if(n==1){
    14             printf("%d\n",n%m);continue;
    15         }
    16         memset(dp,0,sizeof(dp));
    17         dp[1][1] = 1;
    18         for(i=2;i<=n;i++){
    19             for(j=i;j>=1;j--){
    20                 dp[i&1][j] = (dp[i&1][j+1]+dp[(i-1)&1][i-j])%m;
    21             }
    22         }
    23         ans = 0;
    24         for(j=1;j<=n;j++){
    25             ans = (ans+dp[n&1][j])%m;
    26         }
    27         printf("%d\n",(ans+ans)%m);
    28     }
    29     return 0;
    30 }

     

     
  • 相关阅读:
    SpringBoot入门
    Java自定义注解(1)
    git集成idea
    git常用命令
    Shiro授权
    shiro认证
    shiro入门
    SpringMVC文件上传
    SpringMVC入门
    mybatis关联关系映射
  • 原文地址:https://www.cnblogs.com/celia01/p/2611017.html
Copyright © 2011-2022 走看看