面试OR笔试6——条件数列数

来源:互联网 发布:清华 知乎 编辑:程序博客网 时间:2024/05/19 03:46

1 题目及要求

1.1 题目描述

求满足以下条件的数列的个数。

1)数列长度为n

2)数列中每个数都在1k之间(包括1k);

3)对于位置相邻的两个数ABAB前),都满足 A<=B A mod B != 0(满足其一即可)。

例如对n = 4 k = 7,则[1, 7,7, 2]满足,[4, 4, 4, 2]不满足。

给出nk,求满足以上条件的数列的个数。

其中:nk为整数(1n101k10^5

由于答案可能较大,输出时对1000000007取模的结果。

 

1.2 测试用例

1n = 2, k = 2,答案为3

2n = 3, k = 2,答案为4

3n = 3, k = 3,答案为15

2 解答

2.1 题目分析

定义一个矩阵dm,该矩阵为k行k列,对于第i行第j列的元素dm[i][j]我们这样规定其元素的值:

当i≤j 或者 (i+1) mod (j+1) != 0 时dm[i][j]为1;否则为0。

其中0≤i<k,0≤j<k。下标均从0开始。

现在解释为什么这么做。根据题意,数列在某个位置能取什么值跟其前一位置的值有关还和k有关,因此我们用矩阵表示所有的情况。用d[i][j]表示当前一位置的数为i+1时,该位置能否取j+1,能就为1,不能则为0。这样该矩阵dm的第i行就表示前一位置为i+1时,该位置能取哪些值;同理dm的第j列就表示,当前一位置是哪些值时该位置能取j+1。

很容易看出,该矩阵只和k有关,当k确定时,该矩阵就唯一确定。

下面来说明如何通过该矩阵求解该问题。

我们用一k维行向量ret来表示数列某一位置n0可以取得数值以及对应的数列个数。例如 ret[j] = m表示该位置取j+1时对应于m个不同的满足条件的数列。

现在我们把这个向量和dm矩阵相乘看看得到什么,令ret’= ret * dm

则根据矩阵的乘法,ret’也是一个k维行向量,且其第j元素是由ret和dm的第j列相乘得到的,根据ret和dm 的含义,ret’的含义为数列n0的下一位置的可能取值及对应个数。

那n0的下下个位置呢,很简单啊,再乘以一个dm 就行了。

……..

直到得到表示数列的最后一个位置对应的向量即可。最后把该行向量的所有元素求和即为最终答案。(根据向量对应的含义,该位置取所有值的数列的个数之和即为所求)。

还有就是初始值了,第一个位置没有前一位数值,因此可以取1到k的所有值,因此初始ret应为一所有元素都为1的k维行向量。令onek1表示k维列向量,以上可以这样简写:

 

答案= onek1’ * dm ^(n-1) * onek1

 

其中 onek1’ 表示onek1 的转置,即为k维行向量,方阵的0次方为单位矩阵,最后乘以列向量其实就是求和,乘积结果是一标量数值。

 

2.2 代码

vector<vector<int>> vMul(vector<vector<int>> &v1, vector<vector<int>> &v2, int mn = 1000000007) { // 定义矩阵乘法int r1(v1.size()), r2(v2.size());if (r1 < 1 || r2 < 1) return vector<vector<int>>();int c1(v1[0].size()), c2(v2[0].size());if(c1 < 1 || c2 < 1 || c1 != r2) return vector<vector<int>>();vector<vector<int>> ret(r1, vector<int>(c2, 0));for (int k1(0); k1 < r1; ++k1)for (int k2(0); k2 < c2; ++k2) {for (int k3(0); k3 < r2; ++k3)ret[k1][k2] = (ret[k1][k2] + v1[k1][k3] * v2[k3][k2]) % mn;}return ret;}int arrayNum(int n, int k) {vector<vector<int>> dm(k, vector<int>(k, 0)), one1k(1, vector<int>(k, 1)), onek1(k, vector<int>(1, 1));for (int k1(0); k1 < k; ++k1) // 求dm矩阵for (int k2(0); k2 < k; ++k2) {if (!(k2 < k1) || (k1+1)%(k2+1)) dm[k1][k2] = 1;}auto ret(one1k);for (int k1(1); k1 < n; ++k1) {ret = vMul(ret, dm);}ret = vMul(ret, onek1);return ret[0][0];}


 

3 测试

测试较简单,请读者自行测试。
原创粉丝点击