题目描述与示例
题目描述
给定两个只包含数字的数组 a
, b
,调整数组 a
里面数字的顺序,使得尽可能多的 a[i] > b[i]
。数组 a
和 b
中的数字各不相同。
输出所有可以达到最优结果的 a
数组数量
输入描述
输入的第一行是数组 a
中的数字
输入的第二行是数组 b
中的数字
其中只包含数字,每两个数字之间相隔一个空格,a
,b
数组大小不超过 10
输出描述
输出所有可以达到最优结果的 a
数组数量
示例一
输入
11 8 20
10 13 7
输出
1
说明
最优结果只有一个,a = [11, 20, 8]
,故输出 1
示例二
输入
11 12 20
10 13 7
输出
2
说明
有两个 a
数组的排列可以达到最优结果,[12, 20, 11]
和[11, 20, 12]
,故输出 2
示例三
输入
1 2 3
4 5 6
输出
0
说明
a
无论如何都会全输,所以无最优解。
解题思路
最优结果的胜出组数
经过调整后,a
数组中胜利的最大组数max_win_num
,很容易想到用贪心来解决。
max_win_num = 0
a.sort(reverse=True)
b.sort(reverse=True)
idx_a, idx_b = 0, 0
while idx_b < n:
if a[idx_a] > b[idx_b]:
idx_a += 1
idx_b += 1
max_win_num += 1
else:
idx_b += 1
达到最优结果的数组数量
注意到a
,b
数组大小不超过 10
,很容易想到可以用回溯枚举的方式,列举出所有具有max_win_num
组胜利的数组a
的排列。
这里的回溯无非是在数组a
的全排列的基础上,加上若干题意的限制条件。
数组大小最大为10
,一共存在10! = 3628800 ~= 10^6
种排列情况,如果不加以剪枝,全部枚举出来,可能会导致超时。
对于每一层递归,考虑剩余可选择的元素个数rest_not_used = used.count(False)
。本题存在两种剪枝策略:
- 当
rest_not_used
个元素全部都选上,数组a
的胜利总组数rest_not_used + win_num
都无法到达max_win_num
组时,在当前状态继续进行递归已经没有意义了,可以直接剪枝 - 当当前胜利组数已经等于最大胜利组数,即
win_num == max_win_num
成立时,剩余的rest_not_used
个元素一共可以带来factorial(rest_not_used)
种排列,无需通过回溯获得这些排列的具体结果,直接令答案数量加上factorial(rest_not_used)
即可。
from math import factorial
def dfs(a, b, idx_b, n, win_num, used):
global ans
rest_not_used = used.count(False)
if max_win_num > win_num + rest_not_used:
return
if win_num == max_win_num:
ans += factorial(rest_not_used)
return
for idx_a in range(n):
if used[idx_a]:
continue
used[idx_a] = True
dfs(a, b, idx_b+1, n, win_num + int(a[idx_a] > b[idx_b]), used)
used[idx_a] = False
代码
Python
from math import factorial
def dfs(a, b, idx_b, n, win_num, used):
global ans
# 数组a中,还没选择的个数rest_not_used
# 即为used中值为False的个数
# 或者n-idx_b,也是rest_not_used的值
rest_not_used = used.count(False)
# 剪枝:
# 如果rest_not_used个元素全部都选上
# 都无法到达max_win_num组,则无需继续考虑,直接剪枝
if max_win_num > win_num + rest_not_used:
return
# 如果当前排列能够胜利的组数win_num等于max_win_num
# 说明剩余尚未选择的rest_not_used个元素,
# 无论如何排列,都一定是一组要求的答案
# 根据排列组合,剩余的rest_not_used个元素,
# 一共有factorial(rest_not_used)中排列方式
if win_num == max_win_num:
ans += factorial(rest_not_used)
return
# 考虑所有位置
for idx_a in range(n):
# 如果已经使用,则直接跳过
if used[idx_a]:
continue
# 状态更新
used[idx_a] = True
# a[idx_a]和b[idx_b]进行比较
# 如果前者大于后者,则win_num需要+1,否则不变
# 同时idx_b需要+1
dfs(a, b, idx_b + 1, n, win_num + int(a[idx_a] > b[idx_b]), used)
# 回滚
used[idx_a] = False
a = [11, 12, 23, 99]
b = [13, 7, 25, 21]
n = len(a)
# 基于贪心思想,计算出a数组经过排序之后的最大胜利组数
max_win_num = 0
a.sort(reverse=True)
b.sort(reverse=True)
idx_a, idx_b = 0, 0
while idx_b < n:
if a[idx_a] > b[idx_b]:
idx_a += 1
idx_b += 1
max_win_num += 1
else:
idx_b += 1
ans = 0
if max_win_num == 0:
print(ans)
else:
used = [False] * n
dfs(a, b, 0, n, 0, used)
print(ans)