题目阅览
集合M至少有两个元素(实数),且M中任意两个元素差的绝对值都大于2,则称M为“翔集合”,已知集合S={1,2…,n},请求出n的子集中共有多少个翔集合。
输入格式:输入共一行,一个整数n.(n>=2)
输出格式:输出共一行,一个整数表示S的子集中共有多少个翔集合,由于个数可能过大,请输出这个值除以1000007的余数。
样例输入1
4
样例输出1
1
题目简介
我想以从数字1到10开始依次的翔集合来介绍,如下图所示:
以上是1到10以内所有翔集合的个数,满足任意两个元素差的绝对值都大于2,其中f(n)表示包含n的翔集合的个数,f(n-1)表示不包含n的所有翔集合的个数,最终结果就是将两者相加,然后再加上两个元素的集合,对于n来说,也就是n-3,便得出了最终的通项公式:
f(n)=f(n-1)+f(n-3)+n-3 (f(3)=f(2)=0,f(4)=1)
观察上述通项公式:右边有三个变量f(n-1)、f(n-3)、n-3,但f(n-1)和f(n-3)不连续,所以需要补个f(n-2),更加方便矩阵计算,从而我们可以建立一个1*5的矩阵An=|f(n-1) f(n-2) f(n-3) n-3 1|,与此同时需要一个转移矩阵B使得An×B=|f(n) f(n-1) f(n-2) n-2 1|=An+1等式成立,又因为An=A4Bn-4,而A4=|f(4) f(3) f(2) 2 1|=|1 0 0 2 1|,所以只要求得Bn-4就可对矩阵B求快速幂,计算得出矩阵B如下:
思路简介
根据上述得出的通项公式,通过数学转换建立转移变换矩阵快速幂,有关矩阵快速幂计算参照:矩阵快速幂
具体算法思想:首先,根据输入得到需要计算的n值,然后通过上述计算得出的B矩阵进行矩阵快速幂乘法得出结果,再乘以相对应的A矩阵,最后按照要求对1000007取余输出最后的结果。
翔集合实现代码
#include <stdio.h>
#define MOD 1000007
typedef long long ll;
typedef struct ma {
ll m[5][5];
}mat;// 用结构体代替数组方便写函数
mat E = {0}, B = {0};// E为单位矩阵、B为转移矩阵
mat sq(mat a, mat b) {// 求矩阵的平方
mat c;
int i, j, k;
for(i = 0;i < 5; ++i) {
for(j = 0;j < 5; ++j) {
c.m[i][j] = 0;
for(k = 0;k < 5; ++k) {
c.m[i][j] = (c.m[i][j]+a.m[i][k]*b.m[k][j])%MOD;
}
}
}
return c;
}
mat qpow(mat a, ll n) {// 矩阵的快速幂
mat temp = E;
for(;n;n>>=1,a = sq(a, a)) {
if(n&1) {// 若为奇数则需要多乘一次
temp = sq(temp, a);
}
}
return temp;// 返回矩阵a的n次方即矩阵temp
}
int main() {
ll n;
scanf("%lld",&n);
if(n < 4){
printf("0");
return 0;
}
B.m[0][0] = B.m[0][1] = B.m[1][2] = B.m[2][0] = B.m[3][0] = B.m[3][3] = B.m[4][3] = B.m[4][4] = 1;
int i, j;
for(i = 0;i < 5; ++i) {
E.m[i][i] = 1;
}
mat ans = qpow(B, n-4);
ll res = (ans.m[0][0]+2*ans.m[3][0]+ans.m[4][0])%MOD;// 矩阵A={1,0,0,2,1}和res的第一列相乘在取余即可
printf("%lld", res);
return 0;
}
运行截图
注:以上方法参考这位博主的博客:C语言实现 蓝桥杯 算法提高 翔集合