CCF模拟题4-有趣的数

CCF模拟题4-有趣的数

问题描述

我们把一个数称为有趣的,当且仅当:

  1. 它的数字只包含0, 1, 2, 3,且这四个数字都出现过至少一次。
  2. 所有的0都出现在所有的1之前,而所有的2都出现在所有的3之前。
  3. 最高位数字不为0。
    因此,符合我们定义的最小的有趣的数是2013。除此以外,4位的有趣的数还有两个:2031和2301。
    请计算恰好有n位的有趣的数的个数。由于答案可能非常大,只需要输出答案除以1000000007的余数。
    输入格式

输入只有一行,包括恰好一个正整数n (4 ≤ n ≤ 1000)。
输出格式:输出只有一行,包括恰好n 位的整数中有趣的数的个数除以1000000007的余数。

样例输入
4
样例输出
3

一、思路

这里约束了:0与1、2与3的关系

考虑第一个数:这个数只能是2
考虑第二个数:0或者3或者2
这里可以得出一个结论,如果只有两个的话,其中一个必然是2,且另一个不可能是1
第三个数的取值范围与第二个数有关系

那么,如此类推下去,假设长度为n,我们需要取第i个数:
这里你可以将0视为出现1的前提条件,反过来,出现1之后,就不能出现0了。
将2视为出现3的前提条件,反过来,出现3之后,就不能出现2了。
第i个数的取值肯定是和之前有关系的,考虑之前出现过的状态:

(1)只出现了2

这种情况下,我们可以取的数有:0,2,3

(2)出现了2,3

这种情况下,我们可以取的数有:0,3
因为出现了3,3不能在2的前面,所以取不了2了

(3)出现了0,2

这种情况下,我们可以取的数有:0,1,2,3

(4)出现了0,1,2

这种情况下,我们可以取的数有:1,2,3

(5)出现了0,2,3

这种情况下,我们可以取的数有:0,1,3
因为0出现了,所以可以取1了

(6)出现了0,1,2,3四个数字

这种情况下,我们可以取的数有:1,3
因为出现了3,3不能在2的前面,所以取不了2了
因为出现了1,1不能在0的前面,所以取不了0了

注意:有的情况是不可能出现的,比如:(1,2)、(1,3)这种

这里,我们给出如下两个要求:
(1)0只能出现在1前面
(2)2只能出现在3前面
现在我们假设 f ( i , j ) f(i,j) f(i,j)表示:
给定数的长度为i,且之前的状态是j时,所能生成的满足上述两个条件的个数
那么这个 f ( i , j ) f(i,j) f(i,j)与我们要求解的问题有什么关系呢?
没错,我们有趣的数需要满足三个条件,除了上述两个外,第三个条件:
(3)0,1,2,3都要出现
这正好是状态6的情况
因此 f ( i , 6 ) f(i,6) f(i,6)就是长度为 i i i的有趣的数的个数

现在来看看 f ( i , 6 ) f(i,6) f(i,6)是怎么来的,生成 f ( i , 6 ) f(i,6) f(i,6)有几种办法呢?先考虑一步走
无非是:

  • 长度为 i − 1 i-1 i1的时候,到达状态6,即: f ( i − 1 , 6 ) f(i-1,6) f(i1,6),最后一个数从1和3中选一个
  • 长度为 i − 1 i-1 i1的时候,到达状态5,即: f ( i − 1 , 5 ) f(i-1,5) f(i1,5),最后一个数只能是1,这样才能满足条件(3)
  • 长度为 i − 1 i-1 i1的时候,到达状态4,即: f ( i − 1 , 4 ) f(i-1,4) f(i1,4),最后一个数只能是3,这样才能满足条件(3)
    除此以外还有别的方法吗?考虑两步走:
  • 长度为 i − 2 i-2 i2的时候,到达状态6,这种情况下与一步走的情况(1)相同,重复了
  • 长度为 i − 2 i-2 i2的时候,到达状态5,这种情况下,决定下一个数时,会转换为一步走的三种情况中的一个

    其余情况不再累述,事实上两步走终究是要变到一步走的

所以我们推出了状态转移方程:
f ( i , 6 ) = 2 ∗ f ( i − 1 , 6 ) + f ( i − 1 , 5 ) + f ( i − 1 , 4 ) f(i,6)=2*f(i-1,6)+f(i-1,5)+f(i-1,4) f(i,6)=2f(i1,6)+f(i1,5)+f(i1,4)

这个时候,你可能会说, f ( i − 1 , 6 ) 、 f ( i − 1 , 5 ) 、 f ( i − 1 , 4 ) f(i-1,6)、f(i-1,5)、f(i-1,4) f(i1,6)f(i1,5)f(i1,4)也不好算啊

其实,通过上面的公式,原问题的规模从 i i i下降到了 i − 1 i-1 i1,并且复杂度也有下降
f ( i − 1 , 6 ) f(i-1,6) f(i1,6)的计算公式与 f ( i , 6 ) f(i,6) f(i,6)一样

那么就来看看 f ( i − 1 , 5 ) f(i-1,5) f(i1,5)该怎么算了,还是考虑一步走:

  • 长度为 i − 2 i-2 i2的时候,到达状态5,即: f ( i − 2 , 5 ) f(i-2,5) f(i2,5),最后一个数:0或者3
  • 长度为 i − 2 i-2 i2的时候,到达状态2,即: f ( i − 2 , 3 ) f(i-2,3) f(i2,3),最后一个数只能是3,这样才能到状态5
  • 长度为 i − 2 i-2 i2的时候,到达状态3,即: f ( i − 2 , 2 ) f(i-2,2) f(i2,2),最后一个数只能是0,这样才能到状态5

于是:
f ( i − 1 , 5 ) = 2 ∗ f ( i − 2 , 5 ) + f ( i − 2 , 2 ) + f ( i − 2 , 3 ) f(i-1,5)=2*f(i-2,5)+f(i-2,2)+f(i-2,3) f(i1,5)=2f(i2,5)+f(i2,2)+f(i2,3)
同样对 f ( i − 1 , 4 ) f(i-1,4) f(i1,4)分析之后,有:
f ( i − 1 , 4 ) = 2 ∗ f ( i − 2 , 4 ) + f ( i − 2 , 3 ) f(i-1,4)=2*f(i-2,4)+f(i-2,3) f(i1,4)=2f(i2,4)+f(i2,3)

好了,剩下的拆分,也是一样的道理:
f ( i − 2 , 2 ) = f ( i − 3 , 2 ) + f ( i − 3 , 1 ) f(i-2,2) = f(i-3,2)+f(i-3,1) f(i2,2)=f(i3,2)+f(i3,1)
f ( i − 2 , 3 ) = f ( i − 3 , 3 ) + f ( i − 3 , 1 ) f(i-2,3) = f(i-3,3)+f(i-3,1) f(i2,3)=f(i3,3)+f(i3,1)
初始条件:
f ( n , 1 ) = 1 f(n,1)=1 f(n,1)=1

C++代码:

class Solution {
public:
	int findInterestNums(int n) {
		vector<vector<long long> > dp(n + 1, vector<long long>(6));
		// 初始化
		for (int i = 0; i < 6; i++)
			dp[0][i] = 0;
		// 计算
		for (int i = 1; i <= n; i++) {
			long long j = i - 1;
			dp[i][0] = 1;
			dp[i][1] = dp[j][1] + dp[j][0];
			dp[i][2] = dp[j][2] + dp[j][0];
			dp[i][3] = 2 * dp[j][3] + dp[j][2];
			dp[i][4] = 2 * dp[j][4] + dp[j][2] + dp[j][1];
			dp[i][5] = 2 * dp[j][5] + dp[j][4] + dp[j][3];
		}		
		return dp[n][5];
	}
};
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值