题目简述
小扣注意到秋日市集上有一个创作黑白方格画的摊位。摊主给每个顾客提供一个固定在墙上的白色画板,画板不能转动。画板上有 n * n 的网格。绘画规则为,小扣可以选择任意多行以及任意多列的格子涂成黑色(选择的整行、整列均需涂成黑色),所选行数、列数均可为 0。
小扣希望最终的成品上需要有 k 个黑色格子,请返回小扣共有多少种涂色方案。
注意:两个方案中任意一个相同位置的格子颜色不同,就视为不同的方案。
题解示例
示例 1:
输入:n = 2, k = 2
输出:4
解释:一共有四种不同的方案:
第一种方案:涂第一列;
第二种方案:涂第二列;
第三种方案:涂第一行;
第四种方案:涂第二行。
示例 2:
输入:n = 2, k = 1
输出:0
解释:不可行,因为第一次涂色至少会涂两个黑格。
示例 3:
输入:n = 2, k = 4
输出:1
解释:共有 2*2=4 个格子,仅有一种涂色方案。
数据范围
1 <= n <= 6
0 <= k <= n * n
标记难度
难度:简单
通过次数:8065
提交次数:24485
通过率:32.9%
问题解析
本题主要考察组合和阶乘。
假设选择涂黑i行、j列,那么此时黑色方块共有 i*n + j*n - i*j 块,其中 i*j 为行和列重合的块数。
而在n行中取i行、在n列中取j列,即数学中的组合数,其值可以分别为 n! / (i!(n-i)!) 、 n! / (j!(n-j)!) ,这便涉及到阶乘的计算了。
要注意的是,当不涂或者涂满时,方法都只有一种。
python3代码
class Solution:
def paintingPlan(self, n: int, k: int) -> int:
if k == 0 or k == n*n:
return 1
result = 0
for i in range(1, n):
for j in range(1, n):
if n*i + n*j - i*j == k:
result += math.factorial(n)/(math.factorial(i) * math.factorial(n-i))*math.factorial(n)/(math.factorial(j) * math.factorial(n-j))
return int(result)
其中,阶乘的求解可以用递归,也可以用循环,或者直接用库函数,可以分别运行来比较其时间复杂度和空间复杂度。
另外,组合的求解方式也非常多样化,可以利用阶乘函数计算,也可以直接进行数值计算。
# 递归
def factorial(n):
if n == 1:
return 1
else:
return n * factorial(n-1)
# 循环
def factorial(n):
fact = 1
for i in range(2, n+1):
fact *= i
return fact
# 利用阶乘
def C(n, a):
return factorial(n)/(factorial(a) * factorial(n-a))
# 直接计算
def C(n, a):
result = 1
for i in range(n, n-a, -1):
result *= i
for j in range(1, a+1):
result /= j
return result
# 防溢出防除不尽
def C(n, a):
result = 1
for i in range(1, a+1):
result *= (n+1-i)/i
return result
C语言代码
int factorial(int n)
{
if(n == 1)
{
return 1;
}
return n * factorial(n-1);
}
int C(int n, int a)
{
return factorial(n) / (factorial(a) * factorial(n-a));
}
int paintingPlan(int n, int k){
int result = 0, i, j;
if(k == 0 || k == n*n)
{
return 1;
}
for(i = 1; i <= n; ++i)
{
for{j = 1; j <= n; ++j)
{
if((n*i + n*j - i*j) == k)
{
result += C(n, i) * C(n, j);
}
}
}
return result;
}
C++代码(Copy)
class Solution {
public:
//预处理组合数
int C[10][10];
void Init(){
for(int i = 0; i < 10; i++){
for(int j = 0; j <= i; j++){
if(!j) C[i][j] = 1;
else C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
}
}
}
int paintingPlan(int n, int k) {
if(k == 0) return 1;
if(k < n) return 0;
if(k == n * n) return 1;
Init();
int ans = 0;
for(int i = 0 ; i <= n; i++){
for(int j = 0; j <= n; j++){
if(n * i + n * j - i * j == k){
ans += C[n][i] * C[n][j];
}
}
}
return ans;
}
};
大佬专属代码
int paintingPlan(int n, int k)
{
int p=0;
if(k==0)
p=1;
if(n==1)
{
if(k==1)
p=1;
}
if(n==2)
{
if(k==2)
p=4;
if(k==3)
p=4;
if(k==4)
p=1;
}
if(n==3)
{
if(k==3)
p=6;
if(k==5)
p=9;
if(k==6)
p=6;
if(k==7)
p=18;
if(k==8)
p=9;
if(k==9)
p=1;
}
if(n==4)
{
if(k==4)
p=8;
if(k==7)
p=16;
if(k==8)
p=12;
if(k==10)
p=48;
if(k==12)
p=44;
if(k==13)
p=32;
if(k==14)
p=48;
if(k==15)
p=16;
if(k==16)
p=1;
}
if(n==5)
{
if(k==5)
p=10;
if(k==9)
p=25;
if(k==10)
p=20;
if(k==13)
p=100;
if(k==15)
p=20;
if(k==16)
p=100;
if(k==17)
p=200;
if(k==21)
p=150;
if(k==19)
p=200;
if(k==20)
p=10;
if(k==25)
p=1;
if(k==24)
p=25;
if(k==23)
p=100;
if(k==22)
p=100;
}
if(n==6)
{
if(k==36)
p=1;
if(k==35)
p=36;
if(k==34)
p=180;
if(k==33)
p=240;
if(k==32)
p=405;
if(k==31)
p=72;
if(k==30)
p=612;
if(k==28)
p=450;
if(k==27)
p=400;
if(k==26)
p=180;
if(k==24)
p=630;
if(k==21)
p=240;
if(k==20)
p=225;
if(k==18)
p=40;
if(k==16)
p=180;
if(k==12)
p=30;
if(k==11)
p=36;
if(k==6)
p=12;
}
return p;
}
敲黑板!!!
排列、组合、阶乘的各种实现代码必须做到信手拈来,还要考虑其时间复杂度、空间复杂度、是否溢出、是否除不尽等!!!