A
问题描述:
对于一个排列,考虑相邻的两个元素,如果后面一个比前面一个大,表示这个位置是上升的,用I表示,反之这个位置是下降的,用D表示。如排列3,1,2,7,4,6,5可以表示为DIIDID。
现在给出一个长度为n-1的排列表示,问有多少种1到n的排列满足这种表示。
输入:
一个字符串S,S由I,D,?组成.?表示这个位置既可以为I,又可以为D。
输出:
有多少种排列满足上述字符串。输出排列数模1000000007
样例输入:
?D
样例输出:
3
数据范围:
20%的数据 S长度<=10
100%的数据 S长度<=1000
分析:怎么做?先想一想,i+1个数的方法数可以用i个数的方法数推出来,自然就想到了dp。一看数据范围,1000,只可以开二维,多半是二维dp。
dp[i][j] i表示共有i个数,j表示当前序列的最后一位为j。可以发现一个东西:前面i个数范围都在1~i且1~i中的每个数都只出现了1次。得出下列结论:
1.'D' 呈下降趋势,当前这位填j,前面那位可以填j+1~i-1(i表示序列长度,已经+1),但这样的话,前面又有一个j,和当前这位j矛盾。我们可以视作前面所有>=j的数全部+1,来避免这种矛盾,所以当前位为j的前一位实际可以填j~i-1。(这一句重点)
2.'I' 呈上升趋势,当前这位填j,前一位填1~j-1的数。为什么不能填j?因为当前位填j后实际上整个序列是少一个为i(i表示序列长度,已经+1)的数的,我们只能把每个>=j的数视作+1来避免这种bug。如果当前位填j,前一位填j,前面那位会被视作+1,矛盾。
3.'?' D和I的情况加起来,耶。
解决。
CODE:
1 #include<cstdio> 2 int f[1005][1005],mod=1000000007; 3 int main() 4 { 5 freopen("B.in","r",stdin); 6 freopen("B.out","w",stdout); 7 char c;int i=1,ans=0; 8 c=getchar();f[1][1]=1; 9 while(c=='D'||c=='I'||c=='?') 10 { 11 i++; 12 for(int j=1;j<i;j++) 13 f[i-1][j]=(f[i-1][j]+f[i-1][j-1])%mod; 14 if(c=='I'){ 15 for(int j=2;j<=i;j++) 16 f[i][j]=f[i-1][j-1]; 17 } 18 if(c=='D'){ 19 for(int j=1;j<i;j++) 20 f[i][j]=(f[i-1][i-1]-f[i-1][j-1]+mod)%mod; 21 } 22 if(c=='?'){ 23 for(int j=1;j<i;j++) 24 f[i][j]=(f[i-1][i-1]-f[i-1][j-1]+mod)%mod; 25 for(int j=2;j<=i;j++) 26 f[i][j]=(f[i-1][j-1]+f[i][j])%mod; 27 } 28 c=getchar(); 29 } 30 for(int j=1;j<=i;j++) 31 ans=(ans+f[i][j])%mod; 32 printf("%d",ans);return 0; 33 }