CCF 有趣的数201412-4
c++实现
问题描述
我们把一个数称为有趣的,当且仅当:
1. 它的数字只包含0, 1, 2, 3,且这四个数字都出现过至少一次。
2. 所有的0都出现在所有的1之前,而所有的2都出现在所有的3之前。
3. 最高位数字不为0。
因此,符合我们定义的最小的有趣的数是2013。除此以外,4位的有趣的数还有两个:2031和2301。
计算恰好有n位的有趣的数的个数
第一次遇到这个题,我是蒙的,完全没有思路.看了网上的题解再结合一些网络上的资料,发现这道题是典型的动态规划问题.
下面我尝试用通俗易懂的方式解释一下自己的思路
动态规划的特点是:一个大问题可以分解成若干个小问题,然后通过综合多个小问题的最优解得到大问题的的最优解.
这道题目问的是n位有趣的数的个数,那么想得到最终的结果,我们可以怎么做?
借助动态规划思路,n位有趣的数可通过n-1位有趣的数来推断,怎么推断呢?要分情况讨论.题目要求四个数字至少出现一次,n位有趣的数可以通过两种情况来组合:
第一种是前n-1位包含了四个数字,那么第n位只能是0,1.
第二种是前n-1位只包含三个数字.只能是(0,1,2)或(0,2,3).相应的第n位就是3或1.这是为什么呢?题设要求四个数字都出现过至少一次,因此(0,1,3)和(1,2,3)是不合题意的,比如,前面有了0,1,3那么最后一位只能是2,这样导致3出现在2前面.
这时候问题就变成了求n-1位包含(0,1,2)或(0,2,3)有趣的数(暂时忽略四个数字都出现过至少一次 )的个数.用上面同样的思路.比如,n-1位包含(0,2,3)的有趣的数,可通过两种情况组合:
第一种是前n-2位包含了(0,2,3)的有趣的数,第n-1位可以是0,2
第二种是前n-2位包含了(0,2)或(2,3)的有趣的数,相应的第n-1位可以是3和0;
同理继续求n-2位包含了(0,2)或(2,3)的有趣的数的个数,按照这个思路进行下去直到只有1位有趣的数,经过分析只能是2.
分析如下:0不能出现在首位,1不能出现在0前,3也不能出现在2前,所以只能是2.
也就是说第一位只能是2,按照上面的思路倒推即可
先设一个数组num[n][6]
表示长度为n时,有趣的数在不同数字组合情况下的个数.6表示有6种情况
- 只有2 用数组
num[n][0]
表示 - 有2,0 用数组
num[n][1]
表示 - 有2,3 用数组
num[n][2]
表示 - 有2,0,1 用数组
num[n][3]
表示 - 有2,0,3 用数组
num[n][4]
表示 - 有2,0,1,3 用数组
num[n][5]
表示
// 各状态的递归式
int j = i - 1;
// 只有2 : 长度为i的有趣的数的个数等于长度为i-1的有趣的数的个数
num[i][0] = num[j][0];
// 有 2,0 : 长度为i的有趣的数的个数等于长度为i-1的只有2的有趣的数的个数加上有2,0的个数的2倍
// 乘2是因为前面i-1为包含了0,2第n位可以是0,2两种情况所以乘2
num[i][1] = num[j][0] + num[j][1] * 2;
// 有2,3 : 等于长度为i-1的只有2和有2,3的有趣的数的个数和
// 没有乘2是因为前n-1位是2,3,第n位只能是3
num[i][2] = num[j][0] + num[j][2];
// 有2,0,1 :前n-1位有2,0,1的个数的两倍(第n位可以是2,1)和前n-1位有2,0的个数的一倍
num[i][3] = num[j][3] * 2 + num[j][1];
// 有2,0,3 : 前n-1位有2,0,3的个数的两倍(第n位可以是0,3)和前n-1位有2,0或2,3的个数的一倍
num[i][4] = num[j][4] * 2 + num[j][1] + num[j][2];
// 有2,0,1,3 : 前n-1位有2,0,1,3的个数的两倍(第n位可以是0,2)和前n-1位有2,0,1或2,0,3的个数的一倍
num[i][5] = num[j][5] * 2 + num[j][3] + num[j][4];
程序实现
int main() {
int n;
scanf("%d",&n);
long long mod = 1000000007;
long long num[n][6];
memset(num,0,sizeof(num));
num[0][0] = 1;
for(int i = 1; i < n; i++) {
int j = i - 1;
num[i][0] = num[j][0] % mod;
num[i][1] = (num[j][0] + num[j][1] * 2) % mod;
num[i][2] = (num[j][2] + num[j][0]) % mod;
num[i][3] = (num[j][3] * 2 + num[j][1]) % mod;
num[i][4] = (num[j][4] * 2 + num[j][1] + num[j][2]) % mod;
num[i][5] = (num[j][5] * 2 + num[j][3] + num[j][4]) % mod;
}
cout<<num[n-1][5]<<endl;
return 0;
}