HDU 4055 Number String

12 篇文章 0 订阅
4 篇文章 0 订阅

原题地址


题意

给定一个字符序列,要求求对应地合法数字序列的排列数目,满足对应位置的数字的大小关系和字符序列匹配

II 对应 1 2 3

ID 对应 1 3 2 或者 2 3 1

I 表示递增,D表示递减,?表示随意。


题解

    dp[i]是长度为i的排列数,那么考虑大小关系,显然状态转移需要第i-1位的数的值

    dp[i][j]表示长度为i,末尾数字为j的合法排列数。

    这道题有一个障碍,dp[i][]排列的是1-i这i个数,dp[i+1][]排列的是1-i+1这些数,dp[i+1][j],只要j!=i+1,就意味着前i个数不是1-i了,插人了一个i+1,似乎违反了dp无后效性的原则,状态转移不合法。

    事实上,这道题的排列的合法与否,与数字本身是没有关系的,只与数字的相对大小关系有关,所以我们应该这样定义dp[i],i个大小不等的数字的合法排列数,dp[i][j]则是i个数字中第j小的数结尾的合法排列数(好绕啊)


转移方程

I dp[i][j]=sum[i-1][j-1];

D dp[i][j]=sum[i-1][i-1]-sum[i-1][j-1]

? dp[i][j]=sum[i-1][i-1]

sum[i][j]=sigma(dp[i][k],1<=k<=j)


这里有个很微妙的问题,dp[i][j]表示1-i中j来结尾,那么dp[i-1][j]实际上是j+1在结尾,这里要有dp[i][j]是把i插人了i-1序列中并把j给提到了最后位置的想法


(这诡异的题目不知道自己过两个月还能不能看懂……


#include<bits/stdc++.h>

using namespace std;
typedef long long LL;

const int maxn=1000;
const int h=1e9+7;

LL dp[maxn+5][maxn+5];
LL sum[maxn+5][maxn+5];
char s[maxn+5];

int main(void)
{
	#ifdef ex
	freopen ("in.txt","r",stdin);
	#endif
	
	while (scanf("%s",s)!=EOF)
	{
		memset(dp,0,sizeof(dp));
		memset(sum,0,sizeof(sum));
		
		int len=strlen(s);
		dp[1][1]=1;
		sum[1][1]=1;
		
		for (int i=2;i<=len+1;++i)
		{
			if (s[i-2]=='I')
			{
				for (int j=1;j<=i;++j)
				{
					dp[i][j]=sum[i-1][j-1];
				}
			}
			else if (s[i-2]=='D')
			{
				for (int j=1;j<=i;++j)
				{
					dp[i][j]=((sum[i-1][i-1]-sum[i-1][j-1])%h+h)%h;
				}
			}
			else
			{
				for (int j=1;j<=i;++j)
				{
					dp[i][j]=sum[i-1][i-1];
				}
			}
			
			sum[i][1]=dp[i][1];
			for (int j=2;j<=i;++j)
			{
				sum[i][j]=(sum[i][j-1]+dp[i][j])%h;
			}
		}
		
		LL ans=sum[len+1][len+1]%h;
		printf("%I64d\n",ans);
	}
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值