C++ 算法提高 翔集合

题目阅览
 集合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语言实现 蓝桥杯 算法提高 翔集合

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值