HDU 4055 Number String

来源:互联网 发布:sql查询语句表别名 编辑:程序博客网 时间:2024/05/16 14:51

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4055


题意:给一个长度为n的含( d , i , ? )字符串,表示一个1~n+1的排列,表示从第二个位置开始,每个位置和它的前一位的关系。

d表示第i位小于i-1位 , i表示第i位大于i-1位 , ?表示第i位可以随意。


思路:dp[i][j]表示前i个位置表示1~i的一个排列,且第i个数为j的合法方案数。

1、若第i位为"i",那么dp[i][j] = ∑dp[i-1][k]  k∈[1,j-1] 

这里第i位放的j , 若j<i,则j一定会在dp[i-1]的排列中出现 , 比如 i = 5 , j = 3

那么我们dp[5][3] = dp[4][1] + dp[4][2] , 拿dp[4][2]为例 , 3  4  1  2可能是一种合法的排列,若此时第5位放3,那么前四个数中就不能出现3,而多了一个5,前四个数只能放1 2 4 5,那么我们就按照大小关系对照3  4  1  2的排列排好,变成4  5  1  2。


2、若第i位为"d",那么第i-1位的数字要比第i位的数字大。假设第i位放j,若j为i,那么dp[i][j]为0。若j<i , 那么dp[i-1]的排列中一定存在一个j,所以我们把dp[i-1]的所有排列进行一个等价调整,把所以大于等于j的数字都+1,这样dp[i-1]的排列就会把数字j空出来。所以dp[i][j] = ∑dp[i-1][k]  k∈[j,i-1]

若第i位可以随意取,把上面两个加起来即可。

用前缀和表示即可省略掉复杂的转移,dp[i][j]表示前i个位置表示1~i的一个排列,第i个数为1~j的所有合法方案数。


#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <cstdlib>#include <iostream>#include <algorithm>#include <stack>#include <map>#include <set>#include <vector>#include <sstream>#include <queue>#include <utility>using namespace std;#define rep(i,j,k) for (int i=j;i<=k;i++)#define Rrep(i,j,k) for (int i=j;i>=k;i--)#define Clean(x,y) memset(x,y,sizeof(x))#define LL long long#define ULL unsigned long long#define inf 0x7fffffff#define mod 1000000007const int maxn = 1002;char s[maxn];LL dp[2][maxn];int main(){    while( gets(s+1) )    {        int n = strlen(s+1) + 1;        Clean(dp,0);        dp[1][1] = 1;        LL g;        rep(i,2,n)            rep(j,1,i)            {                if ( s[i-1] == '?' )                    g = dp[(i+1)&1][j-1] + dp[(i+1)&1][i-1] - dp[(i+1)&1][j-1];                else if ( s[i-1] == 'D' )                    g = dp[(i+1)&1][i-1] - dp[(i+1)&1][j-1];                else g = dp[(i+1)&1][j-1];                dp[i&1][j] = ( dp[i&1][j-1] + g ) % mod;            }        LL ans = (dp[n&1][n]+mod)%mod;        printf("%lld\n",ans);    }    return 0;}


0 0
原创粉丝点击