在线编程测评–扇形涂色问题
近期在参加某公司的内推时,遇到了在线编程测试,考的就是扇形涂色问题的求解。题目的描述是这样的:
题目描述:将一个圆形划分为N个扇形,现有M中不同的颜色, 要求这N块相邻的区域不同色,问共有多少种不同的涂色方案(N>=1,M>=3)。
**举例说明:
当M=3,N=3时,输出的结果为6;M=5, N=5时,结果为1020。
题目分析
- 当N=1时,说明对圆形不进行划分,那么此时用M种颜色进行涂色时,自然有M种方案。
- 当N=2时,将圆形划分为两部分,此时用M种颜色进行涂色时,总共有 A2M A M 2 种方案。
- 当
N≥3
N
≥
3
时,我们假设
S(n)
S
(
n
)
表示涂色到第n块扇形时的所有方案总数。可以这样分析:首先对第1块扇形进行涂色,共有M种涂色方案,即
S(1,M)=M
S
(
1
,
M
)
=
M
;对第2块进行涂色时,由于相邻的两块的颜色不同,所以第2块有M-1种涂法,即
S(2,M)=M∗(M−1)
S
(
2
,
M
)
=
M
∗
(
M
−
1
)
;以此类推,后面的每块都有M-1种。于是,总共的方案有
M∗(M−1)N−1
M
∗
(
M
−
1
)
N
−
1
种。但是这里面还包含了第1块和第N块涂色可能一样的情况,是不符合题意的,应该舍弃。对于这种情况(第1块和第N块涂色可能一样),可以等价于将第1块和第N块看成一块来涂色,那剩下的扇形,就有
S(N−1)
S
(
N
−
1
)
种。这是一种递归的思想。因此,最后的总数
S(N,M)=M∗(M−1)N−1−S(N−1,M). S ( N , M ) = M ∗ ( M − 1 ) N − 1 − S ( N − 1 , M ) .
代码实现
def fan_paint(m, n):
if m < 3 or n < 1:
return None
# 首先考虑初始条件n=1或2的时候
if n < 3:
return combination(m, 2)
# res = (m-1)**n + (-1)**n * (m-1)
res = m*(m-1)**(n-1) - fan_paint(m, n-1)
return res
# 用递归计算n的阶乘
def factorial(n):
if n == 1:
return 1
return n * factorial(n-1)
# 计算排列组合,从n个里面选择m个进行排列组合
def combination(n, m):
return factorial(n) / factorial(n-m)
if __name__ == '__main__':
M = 5
N = 5
print(fan_paint(M, N))
上面的递推公式 S(N,M)=M∗(M−1)N−1−S(N−1,M) S ( N , M ) = M ∗ ( M − 1 ) N − 1 − S ( N − 1 , M ) ,如果将其进行求解,可以得到 S(N,M)=(M−1)N+(−1)N∗(M−1) S ( N , M ) = ( M − 1 ) N + ( − 1 ) N ∗ ( M − 1 ) ,此时只有M,N,可以不用递归。
备注
如有问题或者别的求解方法,欢迎指教!