综述:
做了作业帮的题目,感觉总体不是很难,题目很常规,遇到了这个扇形染色问题,顺势做一个总结和分析。精华!!!如遇上跪求读完,没有收获你砍我!!!
一个圆分成N
个扇形,用四
种颜色上色,要求相邻两个颜色不同,求有多少种不同的方法。
输入描述:
输入切分成的扇形数量(0< n <20)
输出描述:
输出一个整数,表示上色方法数
示例1:
输入:3
输出:24
题目分析
首先这道题目是将问题简化了,是将一个(n,m)
的问题简化为了<20的n
和4
种颜色。n 是分成的块数,m是颜色的个数。
这个办法是推导出求解公式
,直接代入公式进行计算
就可以。
首先,设A(n)为最后的结果。那么
对第 A1 块扇形,有m种画法,
对第 A2 块扇形,有m-1种画法,
对第 A3 块扇形,有m-1种画法,
(因为只要求相邻的两块颜色不同,所以,只需要看前边的一块就可以了)
对第 A4 块扇形,有m-1种画法,
…………
…………
对第 An 块扇形,有m-1种画法,
既为
m∗(m−1)n−1 m ∗ ( m − 1 ) n − 1 。
但是这时候要分成两类:
当颜色不同时符合题目要求,没有影响,
而当两块相同时,可以将两块看作是一块,这个时候染色方法总数就是 A(n−1) A ( n − 1 ) ,这里可能会有一点点绕,可以想象一下,前面的组合排列方式不变,头尾两块变成了一块,我们需要把这种情况的组合都排除出去。
则, A(n)+A(n−1)=m∗(m−1)n−1 A ( n ) + A ( n − 1 ) = m ∗ ( m − 1 ) n − 1 ,
同时, A(2)=m∗(m−1) A ( 2 ) = m ∗ ( m − 1 ) (只有两块的情况下)
再由, A(n)=m∗(m−1)n−1−A(n−1) A ( n ) = m ∗ ( m − 1 ) n − 1 − A ( n − 1 ) ,两边同时减去 (m−1)n ( m − 1 ) n ,
得, A(n)−(m−1)n=−[A(n−1)−(m−1)n−1] A ( n ) − ( m − 1 ) n = − [ A ( n − 1 ) − ( m − 1 ) n − 1 ] ,
来,我,我们看一下等号两边的式子,会发现是一个有规律的式子,同时是一个可以产生递推的式子,递推法在后面会讲到。
继而得, A(n)−(m−1)n=−[A(n−1)−(m−1)n−1] A ( n ) − ( m − 1 ) n = − [ A ( n − 1 ) − ( m − 1 ) n − 1 ]
=(−1)2[A(n−2)−(m−1)n−2] = ( − 1 ) 2 [ A ( n − 2 ) − ( m − 1 ) n − 2 ]
=(−1)3[A(n−3)−(m−1)n−3] = ( − 1 ) 3 [ A ( n − 3 ) − ( m − 1 ) n − 3 ]
=........ = . . . . . . . .
=(−1)n−3[A(3)−(m−1)3] = ( − 1 ) n − 3 [ A ( 3 ) − ( m − 1 ) 3 ]
=(−1)n−2[A(2)−(m−1)2] = ( − 1 ) n − 2 [ A ( 2 ) − ( m − 1 ) 2 ]
(只剩两块时,m种颜色,共有 A(2)=m∗(m−1) A ( 2 ) = m ∗ ( m − 1 ) 种)
=(−1)n−2[m∗(m−1)−(m−1)2] = ( − 1 ) n − 2 [ m ∗ ( m − 1 ) − ( m − 1 ) 2 ]
=(−1)n(−1)2(m−1) = ( − 1 ) n ( − 1 ) 2 ( m − 1 )
=(−1)n(m−1) = ( − 1 ) n ( m − 1 )
故, A(n)=(−1)n(m−1)+(m−1)n A ( n ) = ( − 1 ) n ( m − 1 ) + ( m − 1 ) n
NND,忽然有了一种做了高考推导题的既视感,事实证明数学是真的很重要的东西啊!~
#include "stdio.h"
#include "math.h"
int main()
{
int split_num;
int color_num=4;
scanf("%d",&split_num);
int kind;
/*若是split_num和color_num的数字很大的话,计算结果
* 可能会超出int型的字符长度,故选择double或long型较好。
* 注意,这里的测试用例数字很小,不会超出int型的长度,
* 同时按照用例,输出为int型,若为double或int还需取整操作。故设置为int型*/
kind = pow(color_num - 1,split_num) + (color_num - 1)*pow(-1,split_num);
//公式 A(n) =(-1)^n (m-1)+(m-1)^n
printf("%d",kind);
}
当使用递归法的时候,我们首先要考虑他的递归边界,既什么时候停止跳出,设递归方法是recurve(int n,int m),参数分别是分块数和颜色数split_num 和 color_num。
这里又有了两种思考。先依照上面这一种方法来继续思考他的递归方法。
方法一是直接推导出了他的终极公式,直接代入进行计算就可以。但是我们其实在只是一开始找到规律的时候就可以使用递归的思想来进行拆解,一步步的化简来解决子问题。
在方法一中,我们得到这样一个结论,
A(n)=m∗(m−1)n−1−A(n−1) A ( n ) = m ∗ ( m − 1 ) n − 1 − A ( n − 1 ) ,
则求解 A(n) A ( n ) 的问题就演变成了求解 A(n−1) A ( n − 1 ) 的问题,这样就可以用递归的思想,循环的调用自己进行计算,碰到边界后再返回。
在新涂第N块扇形时,由于新加的第N块扇形的颜色跟旁边两块相关,我们需要考虑两种情况。
如果旁边两块颜色不同
,那么第N块有M-2种
颜色,由于两块颜色不同,可以把中间的摘出来,其他N-1块再拼成一个圆,不影响讨论
,新的圆形求解问题变成了recurve(n-1,m)的问题,
故总共有recurve(n-1,m)*(m-2)种可能;
如果旁边两块颜色相同
,那么第N块有M-1种
颜色,由于两块颜色相同,相当于是去掉任意一块,在去掉第N块,重新拼成的一个圆形符合要求,不影响讨论
,新的圆形的求解问题变成了recurve(n-2,m)的问题,则总共有recurve(n-2,m)*(m-1)种可能;
整个过程我们可以将它理解成是在从第N块到第1块的拆解,推导。
C++代码实现
#include "stdio.h"
#include "math.h"
int recurve(int n,int m){
int kinds;
if(n==1){
kinds = m;
}
if (n==2){
kinds = m*(m-1);
}
if (n==3){
kinds = m*(m-1)*(m-2);
}
if (n>3){
/*kinds = m*pow(m-1,n-1) - recurve(n-1,m);
.............这是方法2.1的递归公式*/
kinds = recurve(n-1,m)*(m-2) + recurve(n-2,m)*(m-1);
//这是方法2.2的递归公式
/*两个递归公式的区别在于,2.1是将在总的数量中减去不合格的数量
*2.2是分情况讨论,找到符合的情况下的数量进行相加*/
}
return kinds;
}
int main()
{
int split_num;
int color_num=4;
int kind;
scanf("%d",&split_num);
kind = recurve(split_num,color_num);
printf("%d",kind);
}
Java代码实现
import java.util.Scanner;
import static java.lang.StrictMath.pow;
public class tryA {
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int split_num = scan.nextInt();
int color_num = 4;
if(split_num >= 1 && color_num>=1){
double kind = recurve(split_num,color_num);
System.out.printf ("%.0f",kind);
}
}
public static double recurve (int N,int M){
double kind_new = 0;
if(N == 1 && M >=1)
return M;
if(N == 2 && M >=2)
return M*(M-1);
if(N == 3 && M>=3)
return M*(M-1)*(M-2);
if(N >3 && M >=2){
/*kind_new= M*pow(M-1,N-1) - recurve(N-1,M);
*法2.1,该方法在数字较大是优势尤其明显*/
kind_new=recurve(N-1,M)*(M-2) + recurve(N-2,M)*(M-1);
}
return kind_new;
}
}
方法2.1的算法,每个recurve方法只被计算了一次,而方法2.2则会被重复计算,以100为例,画图说明一下两种方法的复杂度。
法一推导过程最复杂,但是编程实现最简单,复杂度也最低,只是一次计算,适合数值比较大的时候,适合数学好的人。
法2.1推导简单,实现也不复杂,优。
法2.2为优化前复杂度偏高,思考过程也有点复杂,未改良前,良 - 。
总的数值减去不符合的 比 符合的情况依次相加 要简单不少。
这个思维可以多多使用。 个人意见,未经验证,慎采纳! 个 人 意 见 , 未 经 验 证 , 慎 采 纳 !