C++蓝桥杯 算法训练之K好数

C++ 蓝桥杯题目讲解汇总(持续更新)


VIP试题 K好数

资源限制

时间限制:1.0s 内存限制:256.0MB

问题描述

如果一个自然数N的K进制表示中任意的相邻的两位都不是相邻的数字,那么我们就说这个数是K好数。求L位K进制数中K好数的数目。例如K = 4,L = 2的时候,所有K好数为11、13、20、22、30、31、33 共7个。由于这个数目很大,请你输出它对1000000007取模后的值。

输入格式

输入包含两个正整数,K和L。

输出格式

输出一个整数,表示答案对1000000007取模后的值。

样例输入

4 2

样例输出

7

数据规模与约定

对于30%的数据,KL <= 106;

对于50%的数据,K <= 16, L <= 10;

对于100%的数据,1 <= K,L <= 100。

解题思路

首先理解题意很重要,为什么这么说呢,因为样例中的4进制2位数,4进制分别由0123构成,在这里两位的话就是0123的排列组合 A 2 4 A_{2}^{4} A24 种可能,对于其中的好数有{00,02,03,11,13,20,22,30,31,33}按理说共有10个,但是由于进制数开头不能为0,所以00、01、03无效,所以答案共有7个,这是题意理解。

下面是思路,思路的话,肯定不能通过单纯的找规律,比较那么大的进制肯定不现实,比如50进制,对50取余,0-49的数字,怎么算一位就成问题了,这里的话我们可以根据题目的类型,使用动态规划的方法处理,首先明白动态规划的核心是状态和状态方程

这里我们根据题意构建一个二维数组,dp[l][k],l代表位数,k代表进制,我采用的dp方法是根据前一个数的最后一位数来进行递归的,dp[i][j]代表的是长度为i,结尾是j的 情况下 好数的数目,对于长度为i+1的数,我们在长度为i的数后面插入一个新的值 n u m ∈ ( 0 , k − 1 ) num\in (0,k-1) num(0,k1),这个值和当前长度为i的数的最后一位数比较,一旦最后一位数 j ! = n u m − + 1 j!=num_{-}^{+}1 j!=num+1的话,就是一个好数,而这一切的基础都是建立在dp[i][j]上的,前一个数继承的好数数目,下一个长度+1的数会继承前者的基础,并不断更新,这里就是状态的变化,用公式表示:

$dp[i][m]=\sum^{j=k-1}_{j=0}dp[i-1][j]$,从0到k-1每一种情况都要计算在内

细节上

  • 初始化的时候需要注意的是,无论几进制,以谁为结尾,只要长度为1,那么它的好数个数就是1
  • 最后还要处理不为0开头的情况,而这个过程是在长度为1的时候,存在以0为开头的数的好0数为1,那么这里接下来无论怎么计算,以0开头的情况,输出的时候不能一起计算在内了

状态图

下面以 4 2 的输入为例子的流程,同时对于dp[l][0]不能输出做出直观的解释,因为它是来自于0开头的排列组合数:

j=0 j=1 j=2 j=3
i=1 1 1 1 1
i=2 0+1+1+1 0+1+1 0+1+1 0+1+1+1
原因 j为0时:好数为00,02,03,
对应的dp[1][0]=1,所以加了3次
j为1时:好数为 11,13,
对应的dp[1][1]=1,所以加了2次
j为0时:好数为20,22,
对应的dp[1][0]=1,所以加了2次
j为0时:好数为30,31,33,
对应的dp[1][0]=1,所以加了3次

代码-C++

#include
#include
#define mod	1000000007
using namespace std;


// 好数数组,lst[i][j],长度为i以j为尾数 的情况下 好数的数目 
int dp[105][105];

int main(){
	
	int k,l;
	cin>>k>>l; 
	memset(dp,0,sizeof dp);
	
	//初始化,对于任意进制,位数位1的情况,好数个数都是1 
	for(int j=0;j<k;j++)
		dp[1][j]=1;
	
	for(int i=2;i<=l;i++)	//下面是长度>=2的情况下好数的数目 
		for(int num=0;num<k;num++)	//num代表的是要放入的数字,
			for(int j=0;j<k;j++)	//与长度为i-1,以j为结尾的数比较 
				if(j!=num+1&&j!=num-1){
					dp[i][num]+=dp[i-1][j];
					dp[i][num]%=mod;
				} 
	
	int res=0;	
	//由于进制不能以 0 开头,而dp[l][0]代表的就是以 0 开头的数字,在0后面开始不断插数字
	//不理解的看上面的流程图表
	for(int j=1;j<k;j++){
		res+=dp[l][j];
		res%=mod;
	}
	
//	for(int i=1;i<=l;i++){
//		for (int j=0;j
//			cout<
//		}
//		cout<
//	}
	
	cout<<res;
	return 0;
}

总结

我平时也只是了解一点点的动态规划基础知识,这里说实话第一时间没有想到,刚开始甚至连题目都没有读懂,但是1天多加上不断摸鱼也算是理解了这里的dp方法原理,根据理解的状态方程,从而根据每一个子结构得出的我们需要的结果,这个就是此题解法,每一个长度下的好数数目肯定就是以(0,k-1)这k类结果,只要在这k类结果上不断计算就能得出我们需要的答案了。

你可能感兴趣的:(蓝桥杯)