Number String HDU - 4055(计数dp)

题意:

给你一个字符串s,s[i] = 'D’表示排列中a[i] > a[i+1],s[i] = 'I’表示排列中a[i] < a[i+1]。
比如排列 {3, 1, 2, 7, 4, 6, 5} 表示为字符串 DIIDID
思路:
本来想着dp[i][j]用来统计第i个数为j时情况数量,但是好像不可行。参考了众多大神的博客,用dp[i][j]代表1-i的排列(!!!!注意是排列)其中最后一位为j的情况数量。这样一来状态的转移就有迹可循了。
1.如果st[i - 1] == 'I’的情况,即第i个数大于前一个数,则:
dp[i][j] = dp[i - 1][1] + dp[i - 1][2]… + dp[i - 1][j - 1];
我按照自己的想法尝试解释一下。首先,肯定要加上前一个数的所有情况(如果比当前枚举的数小的话),这很好理解。至于能从1–i - 1的排列转移到1–i的排列的原因在此阐述一下。dp[i - 1](就不写第二维了)代表1 — i - 1的排列,而所枚举的j一定会出现比i - 1小的情况,那么必定会和前面的排列重复(举个例子,之前的排列为1, 3, 2, 4, 现在枚举j为3,把1,3,2,4中大于等于3的数都加上1就相当与把1–i - 1的排列转移到了1–i的排列)。
2.st[i - 1] == 'D’的情况,即第i个数小于前一个数,其实是与上一个情况相似的。
dp[i][j] = dp[i - 1][j] + dp[i - 1][j + 1]… + dp[i - 1][i];
可能你会有疑问了,为什么要加dp[i - 1][j],不是要大于第i个数吗?请仔细想想,我们枚举第i个数之后会把大于等于这个数的数都加1以达到状态的转移,这样一来不就大于j了。。

#include 
#include 
#include 

using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;

char st[1010];
ll dp[1010][1010];
ll f[1010][1010];


int main()
{
   // freopen("in.txt", "r", stdin);
	while(cin >> (st + 1))
	{
		int n = strlen(st + 1) + 1;
		memset(dp, 0, sizeof(dp));
		memset(f, 0, sizeof(f));
		dp[1][1] = f[1][1] = 1;
		ll la = 1;
		for(int i = 2; i <= n; ++ i)
		{
            ll cnt = 0;
            for(int j = 1; j <= i; ++ j)
            {
                if(st[i - 1] == 'I')     //这个数比前一个大
                {
                    dp[i][j] = (dp[i][j] + f[i - 1][j - 1]) % mod;
                }
                else if(st[i - 1] == 'D')
                {
                    dp[i][j] = (dp[i][j] + la - f[i - 1][j - 1] + mod) % mod;
                }
                else
                {
                    dp[i][j] = (dp[i][j] + la) % mod;
                }
                f[i][j] = (f[i][j - 1] + dp[i][j]) % mod;
                cnt = (cnt + dp[i][j]) % mod;
            }
            la = cnt;
		}
		cout << f[n][n] << endl;
	}
	return 0;
}

你可能感兴趣的:(线性dp)