特殊数列个数

满足条件的数列个数

最近碰到了一个很有意思的题目:给定一个正整数 N N N,要求依次选择 1 至 N 1至N 1N这N个数组成一个数列(新选择的数字放在数列末尾),每个数列最开始有元素0。每次选择需要满足两个条件:

  • 数列相邻两个数的差不能超过2
  • 数列最后一个数必须是 N N N

根据输入的N,求最后输出不一样的数列个数

例如:给定正整数 N = 3 N=3 N=3,满足条件的序列有两种(123/213):
N=3情况

N = 4 N=4 N=4时,情况较为复杂,此时的可能情况有3种(1234/1324/2134):
N=4情况

注:红色方块标注违反了两个条件中的一个或多个。

通过上面两个例子可以看出,随着 N N N的增大,数列的组合情况指数级增大,我们通过枚举的方式很难去一一判别每种情况是否满足条件1和条件2。不过题目仅要求输出不同的数列个数,并未要求输出具体的每个数列形式,可以采用动态规划的思想来解决这个问题。

  • 我们记满足条件1,2的以 N N N结尾的数列个数为 f ( N ) f(N) f(N),首先思考在满足条件2的情况下,即以 N N N结尾的数列的前一位有哪几种情况。显然,由于条件1的限制,以N结尾的前一位只能是 N − 1 N-1 N1或者 N − 2 N-2 N2(保证相邻两位的差不超过2)。若是前一位为 N − 1 N-1 N1,则此时后数列最后两位为 N − 1 N-1 N1 N N N的数列个数即为 f ( N − 1 ) f(N-1) f(N1).
  • 若是倒数第二位为 N − 2 N-2 N2,则我们需要思考 N − 1 N-1 N1可以插入的位置。如图所示,若倒数第二位为 N − 2 N-2 N2,则N-1只能放入 N − 3 N-3 N3 N − 2 N-2 N2之间。如果放入 N − 4 N-4 N4 N − 3 N-3 N3之间,则 N − 4 N-4 N4 N − 1 N-1 N1相邻违反条件1.换言之,如果倒数第二位为 N − 2 N-2 N2,则倒数第三位只能固定为 N − 1 N-1 N1,此时以 N N N结尾的满足条件的数列个数为 f ( N − 3 ) f(N-3) f(N3)
    插入位置描述

综上,以N结尾的满足条件的数列分为了互不相交的两类:(…, N − 1 , N N-1,N N1N)和(…, N − 3 , N − 1 , N − 2 , N N-3,N-1,N-2,N N3,N1,N2,N)经过以上分析,我们可以得到状态转移方程: f ( N ) = f ( N − 1 ) + f ( N − 3 ) f(N)=f(N-1)+f(N-3) f(N)=f(N1)+f(N3)

以上的推导只考虑一般情况,现在需要考虑边界条件。显然,我们只有在 N − 3 > 0 N-3>0 N3>0时才能用状态转移方程。故需要提前给出 f ( 1 ) 、 f ( 2 ) 、 f ( 3 ) f(1)、f(2)、f(3) f(1)f(2)f(3)的值。

  • 根据以上推导,现给出Java代码(代码中dp[i]对应上文的 f ( i − 1 ) f(i-1) f(i1),并防止结果过大进行了取余操作):
import java.util.Scanner;

public class SpecialListNum {
	public static int solve(int n) {
		int[] dp=new int[n];
		if(n<3)
			return 1;
		if(n==3)
			return 2; 
		dp[0]=1;
		dp[1]=1;
		dp[2]=2;
		for(int i=3;i<n;i++) {
			dp[i]=(dp[i-1]+dp[i-3])%998244353;
		}
		return dp[n-1]%998244353;
	}
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		Scanner in = new Scanner(System.in);
		int n=in.nextInt();
		in.close();
		System.out.println(solve(n));
	}
}

博客中相关问题欢迎询问探讨,相互学习。QQ:1476154032,以上。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值