一、题目
二、解题
1.动态规划
动态规划最核心的思想,就在于拆分子问题,记住过往,减少重复计算。
1.拆分子问题:
我们要求n位有趣数,如果我们先知道n-1位有趣数,或者n-1位非有趣数(此时的数字如果加上一种数字刚好变成有趣数,这里我们称为类有趣数
),这两种数字数字加上一位,变成n位,就是我们要知道的n位有趣数了。
计算恰好有n位的有趣的数的个数。这里参考Sirm23333大佬的提出的类有趣数
的概念,即暂时不需要满足题目中的条件一。
举个例子,我们需要求一个5位数的有趣数,那么我们需要知道4位的有趣数和类有趣数,然后按照有趣数的规则加上一位,就变成5位数的有趣数字了。依次类推,我们需要知道4位数的有趣数,那我们需要知道3位数的类有趣数…所以我们就需要找到1位,2位,3位的类有趣数,4位数字有趣数和类有趣数…下面就是推导过程:
- 只含有一种数字,(0,1,2,3)4种可能,但是满足题目条件二和条件三的,此时只能是2,所以
状态0:只含有数字2
- 含有两种数字,(01,02,03,12,13,23)6种可能,但是实际上,只有02,23,所以
状态1:2,0
,状态2:2,3
- 含有3种数字,且满足题目条件二和条件三,只能是021或者023,所以
状态3:2,0,1
,状态4:2,3,0
, - 含有4种数字,此时就是有趣数了,
状态5:2,3,0,1
另外,对于一个本身就是有趣数的数字,我们添加一位使得它还是有趣数,只能在末尾加1或3.
而对于一个3位的类有趣数,只能是021或023,想要变成有趣数,包含021的三位数需要添加3,包含023的有趣数需要添加1。
其他状态见代码中。
2.代码
代码如下(示例):
#include<iostream>
#include<cstring>
using namespace std;
const long long MOD=1000000007;
long long status[1001][6];
int main(){
int n;
cin>>n;
memset(status,0,sizeof(status));
status[1][0]=1;
for(int i=2;i<=n;i++){
status[i][0]=1;
status[i][1]=(status[i-1][1]*2 + status[i-1][0]) % MOD;
status[i][2]=(status[i-1][2] + status[i-1][0]) % MOD;
status[i][3]=(status[i-1][3]*2 + status[i-1][1]) % MOD;
status[i][4]=(status[i-1][4]*2 + status[i-1][1] + status[i-1][2]) % MOD;
status[i][5]=(status[i-1][5]*2 + status[i-1][3] + status[i-1][4]) % MOD;
}
cout<<status[n][5]<<endl;
return 0;
}
3.提交结果
总结
动态规划是一种解决复杂问题的方法,它将问题分解为更小的子问题,然后通过解决这些子问题来解决原问题。处理动态规划算法问题的步骤通常包括以下几个步骤:
- 定义状态:确定问题的状态,以及状态之间的转移关系。
- 确定状态转移方程:根据状态之间的转移关系,确定状态转移方程。
- 初始化:确定初始状态的值。
- 计算:根据状态转移方程和初始状态,计算出所有状态的值。
- 输出结果:根据计算出的状态值,输出最终结果。