[CF 1068D] Array Without Local Maximums

题目描述:

有N个数字,有的数字为Ai(1<=Ai<=200),有的数字为-1,-1表示这个数字不确定。
但是这N个数字满足两个性质。
1:每个数字只能为[1,200]中的一个数字
2:每个数字的旁边两个数字至少有一个是大于等于这个数字的
现在让你求这N个数字的可能出现的方案数

题目分析:

典型的DP。
dp[i][j][0/1/2] 表示第 i 这个位置填 j 数字 并且与它左边的数字的大小关系为 k 的方案数有多少种
0表示i比i-1大,1表示i跟i-1一样大,2表示i比i-1小
dp[i][j][1]=dp[i-1][j][0]+dp[i-1][j][1]+dp[i-1][2] 因为i这个数字跟i-1一样大,那么i-1就满足了条件,所以不论i-1与i-2的关系如何,都是符合要求的。
d p [ i ] [ j ] [ 0 ] = ∑ k = 1 j − 1 d p [ i − 1 ] [ k ] [ 0 / 1 / 2 ] dp[i][j][0]=\sum_{k=1}^{j-1} dp[i-1][k][0/1/2] dp[i][j][0]=k=1j1dp[i1][k][0/1/2]因为i这个数字比i-1的数字大,那么i-1就满足了条件,所以不论i-1与i-2的关系如何,都是符合要求的。
d p [ i ] [ j ] [ 0 ] = ∑ k = j + 1 n d p [ i − 1 ] [ k ] [ 1 / 2 ] dp[i][j][0]=\sum_{k=j+1}^{n} dp[i-1][k][1/2] dp[i][j][0]=k=j+1ndp[i1][k][1/2]因为i这个数字比i-1小,那么i-1就不能指望i来满足条件,于是只能转移i-1中已经满足了条件的方案,也就是1/2的方案了。
对于后面两个转移方程,明显一个是前缀,一个是后缀,直接用一个量记录就好了。
我们发现i的状态只与i-1有关系,所以我们可以去掉第一维,用两个数组滚动节省空间。

题目链接:

CF 1068D

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define int long long
const int maxm=100010;
const int mod=998244353;
const int M=200;
int dp[3][210][3];
int val[maxm];
int n;
signed main()
{
	 scanf("%lld",&n);
	 for(int i=1;i<=n;i++) scanf("%lld",&val[i]);
	 for(int i=1;i<=200;i++) 
	 {
	 	if((val[1]==-1)||(i==val[1])) dp[1][i][0]=1;
	 	else dp[1][i][0]=0;
	 }
	 int pre=1,nex=2;
	 for(int i=2,now;i<=n;i++)
	 {
	 	for(int j=1;j<=M;j++) 
	 	{
	 	  if((val[i]==-1)||(j==val[i])) dp[nex][j][1]=(dp[pre][j][0]+(dp[pre][j][1]+dp[pre][j][2])%mod)%mod;
		  else dp[nex][j][1]=0;	
		}
	    now=0;
	    for(int j=1;j<=M;j++)
	    {
	    	if((val[i]==-1)||(j==val[i])) dp[nex][j][0]=now;
	    	else dp[nex][j][0]=0;
	        now=(now+(dp[pre][j][0]+(dp[pre][j][1]+dp[pre][j][2])%mod)%mod)%mod;
		}
		now=0;
		for(int j=M;j>=1;j--)
		{
			if((val[i]==-1)||(j==val[i])) dp[nex][j][2]=now;
			else dp[nex][j][2]=0;
			now=(now+dp[pre][j][1]+dp[pre][j][2])%mod;
		}
		if(pre==1) pre=2,nex=1;
		else pre=1,nex=2;
	 }
	 int ans=0;
	 for(int i=1;i<=200;i++) ans=(ans+(dp[pre][i][1]+dp[pre][i][2])%mod)%mod;
	 return printf("%lld\n",ans)*0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值