3426.所有安放棋子方案的曼哈顿距离
难度:困难
问题描述:
给你三个整数m,n和k。
给你一个大小为mxn的矩形格子,它包含k个没有差别的棋子。请你返回所有放置棋子的合法方案中,每对棋子之间的曼哈顿距离之和。
一个合法方案指的是将所有k个棋子都放在格子中且一个格子里至多只有一个棋子。
由于答案可能很大,请你将它对109+7取余后返回。
两个格子(xi,yi)和(xj,yj)的曼哈顿距离定义为|xi-xj|+|yi-yj|。
示例1:
输入:m=2,n=2,k=2
输出:8
解释:
放置棋子的合法方案包括:
前4个方案中,两个棋子的曼哈顿距离都为1。
后2个方案中,两个棋子的曼哈顿距离都为2。
所以所有方案的总曼哈顿距离之和为1+1+1+1+2+2=8。
示例2:
输入:m=1,n=4,k=3
输出:20
解释:
放置棋子的合法方案包括:
第一个和最后一个方案的曼哈顿距离分别为1+1+2=4。
中间两种方案的曼哈顿距离分别为1+2+3=6。
所以所有方案的总曼哈顿距离之和为4+6+6+4=20。
提示:
1<=m,n<=105
2<=m*n<=105
2<=k<=m*n
问题分析:
这个问题的难度系数比较高,leetcode的问题得分在6分以上即为困难,而完成这个题可以得8分。
追求本心,自然处理,符合人们解决问题的思考逻辑,这个题也是这样来考虑的,而且也确实解决了问题。
首先要解决的是,在一个mXn矩阵中的所有位置放置k个棋子,共有多少种不同的放置法,这又是一个排列组合问题。函数get_all(n,k)解决了这个问题,其的n表示矩阵中的n个位置,k表示放置k个棋子,用字符1表示有棋子,0表示没有棋子,通过递归处理将不同的放置方案求出,返回不同排列的放置方案列表。
其次是将这些不同排列放置方案中的一个进行解析,将放置了棋子的位置转换为mXn矩阵中的坐标并形成坐标列表返回,没有放置棋子的则不予处理。函数get_one_point_grid(m,n,c)解决了这个问题,其中m、n表示矩阵有m行n列,c为上一步求出的放置方案列表中的一个方案。
第三步则将所有的放置方案列表中的方案转化成坐标列表的形式并返回,函数get_all_grid(m,n,k)实现了这一要求,返回的列表中的每一个元素还是一个列表,对应一种放置方案解析出的放置了棋子的坐标序列。
此外还有如何计算两个坐标的曼哈顿值,函数get_distance(point1,point2)解决了这一问题。
计算一种放置方案的曼哈顿值之和用函数get_one_mhd(point_arr)解决。
最后是计算所有放置方案的曼哈顿值之和,函数get_all_mhd(b)解决了这个问题。其返回值即是这个问题的求解。
程序如下:
#将一个字符0插入到k个元素中形成的全排列
def get_in(ak):
n=len(ak)
e = []
for i in range(n+1):
b='0'
a=ak[0:i]
c=ak[i:]
d=a+b+c
e.append(d)
return e
#生成n个位置有k个棋子的各种排列,其中n=行X列,用1表示放置了棋子,0表示未放置棋子
def get_all(n,k):
if n==k:
return ['1'*k]
elif n==k+1:
return get_in('1'*k)
else:
c=[]
for i in get_all(n-1,k):
c.extend(get_in(i))
return list(set(c))
#将放置方案列表中的一个方案解析为mXn矩阵中的坐标,并返回坐标列表
def get_one_point_grid(m,n,c):
a=[]
count=0
for i in range(m): #i表示行数
for j in range(n): #j表示列数
#如果字符串中该字符为1,提取该位置坐标,并加入到a列表中
if c[count]=='1':
a.append([i,j])
count=count+1
return a
#将放置方案列表中的所有方案解析为矩阵坐标列表并以更大的列表返回
def get_all_gird(m,n,k):
a = get_all(m * n, k)
print('以字符形式形成的方案:',a)
b = []
for i in a:
b.append(get_one_point_grid(m, n, i))
print('字符串方案转换为坐标列表方案:',b)
return b
#计算两个格子的曼哈顿距离
def get_distance(point1,point2):
x1=point1[0]
y1=point1[1]
x2=point2[0]
y2=point2[1]
return abs(x1-x2)+abs(y1-y2)
#计算以坐标列表形成的方案列表中的一种方案的曙哈顿距离之和
def get_one_mhd(point_arr):
s=0
n=len(point_arr)
for i in range(n-1):
for j in range(i+1,n):
point1=point_arr[i]
point2=point_arr[j]
s=s+get_distance(point1,point2)
print(f'正在处理坐标方案{point_arr},曼哈顿距离之和为{s}')
return s
#计算所有坐标列表方案的曼哈顿距离之知
def get_all_mhd(b):
s=0
for i in b:
s=s+get_one_mhd(i)
return s
#主程序
m,n,k=eval(input('pls input m,n,k='))
b=(get_all_gird(m,n,k))
print('所有方案的总曼哈顿距离之和为:',get_all_mhd(b))
运行实例一
pls input m,n,k=1,4,3
以字符形式形成的方案: ['0111', '1011', '1101', '1110']
字符串方案转换为坐标列表方案: [[[0, 1], [0, 2], [0, 3]], [[0, 0], [0, 2], [0, 3]], [[0, 0], [0, 1], [0, 3]], [[0, 0], [0, 1], [0, 2]]]
正在处理坐标方案[[0, 1], [0, 2], [0, 3]],曼哈顿距离之和为4
正在处理坐标方案[[0, 0], [0, 2], [0, 3]],曼哈顿距离之和为6
正在处理坐标方案[[0, 0], [0, 1], [0, 3]],曼哈顿距离之和为6
正在处理坐标方案[[0, 0], [0, 1], [0, 2]],曼哈顿距离之和为4
所有方案的总曼哈顿距离之和为: 20
运行实例二
pls input m,n,k=2,2,2
以字符形式形成的方案: ['0101', '1010', '0110', '1001', '1100', '0011']
字符串方案转换为坐标列表方案: [[[0, 1], [1, 1]], [[0, 0], [1, 0]], [[0, 1], [1, 0]], [[0, 0], [1, 1]], [[0, 0], [0, 1]], [[1, 0], [1, 1]]]
正在处理坐标方案[[0, 1], [1, 1]],曼哈顿距离之和为1
正在处理坐标方案[[0, 0], [1, 0]],曼哈顿距离之和为1
正在处理坐标方案[[0, 1], [1, 0]],曼哈顿距离之和为2
正在处理坐标方案[[0, 0], [1, 1]],曼哈顿距离之和为2
正在处理坐标方案[[0, 0], [0, 1]],曼哈顿距离之和为1
正在处理坐标方案[[1, 0], [1, 1]],曼哈顿距离之和为1
所有方案的总曼哈顿距离之和为: 8
运行实例三
pls input m,n,k=1,4,4
以字符形式形成的方案: ['1111']
字符串方案转换为坐标列表方案: [[[0, 0], [0, 1], [0, 2], [0, 3]]]
正在处理坐标方案[[0, 0], [0, 1], [0, 2], [0, 3]],曼哈顿距离之和为10
所有方案的总曼哈顿距离之和为: 10
运行实例四
pls input m,n,k=5,1,5
以字符形式形成的方案: ['11111']
字符串方案转换为坐标列表方案: [[[0, 0], [1, 0], [2, 0], [3, 0], [4, 0]]]
正在处理坐标方案[[0, 0], [1, 0], [2, 0], [3, 0], [4, 0]],曼哈顿距离之和为20
所有方案的总曼哈顿距离之和为: 20
感悟:
将大问题分解为小问题,将数据处理成自己想要的形式,构建数据为解决问题服务,问题的解决将变得有迹可循,富于条理。