Number String HDU - 4055(计数dp)

本文介绍了一种使用动态规划(DP)解决特定排列问题的方法。针对一个由'D'和'I'组成的字符串,其中'D'表示降序,'I'表示升序,文章详细解释了如何通过DP算法计算出所有符合该序列的数字排列数量。通过巧妙地定义状态转移方程,解决了传统DP方法在本问题上的局限性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

给你一个字符串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 <iostream>
#include <cstdio>
#include <cstring>

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值