2021-10-26:给定一个数组arr,arr[i] = j,表示第i号试题的难度为j。给定一个非负数M。想出一张卷子,对于任何相邻的两道题目,前一题的难度不能超过后一题的难度+M。返回所有可能的卷子种数。
答案2021-10-26:
方法1:递归。纯暴力方法,生成所有排列,一个一个验证。 方法2:从左往右的动态规划 + 范围上二分。时间复杂度O(N * logN)。 方法3:从左往右的动态规划 + IndexTree。时间复杂度O(N * logV)。
代码用golang编写。代码如下:
package main
import (
"fmt"
"math"
"sort"
)
func main() {
if true {
arr := []int{1, 5, 3, 4, 2}
ret := ways1(arr, 3)
fmt.Println(ret)
}
if true {
arr := []int{1, 5, 3, 4, 2}
ret := ways2(arr, 3)
fmt.Println(ret)
}
if true {
arr := []int{1, 5, 3, 4, 2}
ret := ways3(arr, 3)
fmt.Println(ret)
}
}
// 纯暴力方法,生成所有排列,一个一个验证
func ways1(arr []int, m int) int {
if len(arr) == 0 {
return 0
}
return process(arr, 0, m)
}
func process(arr []int, index int, m int) int {
if index == len(arr) {
for i := 1; i < index; i++ {
if arr[i-1] > arr[i]+m {
return 0
}
}
return 1
}
ans := 0
for i := index; i < len(arr); i++ {
arr[index], arr[i] = arr[i], arr[index]
ans += process(arr, index+1, m)
arr[index], arr[i] = arr[i], arr[index]
}
return ans
}
// 时间复杂度O(N * logN)
// 从左往右的动态规划 + 范围上二分
func ways2(arr []int, m int) int {
if len(arr) == 0 {
return 0
}
//Arrays.sort(arr);
sort.Ints(arr)
all := 1
for i := 1; i < len(arr); i++ {
all = all * (num(arr, i-1, arr[i]-m) + 1)
}
return all
}
// arr[0..r]上返回>=t的数有几个, 二分的方法
// 找到 >=t 最左的位置a, 然后返回r - a + 1就是个数
func num(arr []int, r int, t int) int {
i := 0
j := r
m := 0
a := r + 1
for i <= j {
m = (i + j) / 2
if arr[m] >= t {
a = m
j = m - 1
} else {
i = m + 1
}
}
return r - a + 1
}
// 时间复杂度O(N * logV)
// 从左往右的动态规划 + IndexTree
func ways3(arr []int, m int) int {
if len(arr) == 0 {
return 0
}
max := math.MinInt64
min := math.MaxInt64
for _, num := range arr {
max = getMax(max, num)
min = getMin(min, num)
}
itree := NewIndexTree(max - min + 2)
//Arrays.sort(arr);
sort.Ints(arr)
a := 0
b := 0
all := 1
itree.add(arr[0]-min+1, 1)
for i := 1; i < len(arr); i++ {
a = arr[i] - min + 1
if a-m-1 >= 1 {
b = i - itree.sum(a-m-1)
} else {
b = i
}
//b = i - (a - m - 1 >= 1 ? itree.sum(a - m - 1) : 0);
all = all * (b + 1)
itree.add(a, 1)
}
return all
}
func getMax(a, b int) int {
if a > b {
return a
} else {
return b
}
}
func getMin(a, b int) int {
if a < b {
return a
} else {
return b
}
}
// 注意开始下标是1,不是0
type IndexTree struct {
tree []int
N int
}
func NewIndexTree(size int) *IndexTree {
res := &IndexTree{}
res.N = size
res.tree = make([]int, size+1)
return res
}
func (this *IndexTree) sum(index int) int {
ret := 0
for index > 0 {
ret += this.tree[index]
index -= index & -index
}
return ret
}
func (this *IndexTree) add(index, d int) {
for index <= this.N {
this.tree[index] += d
index += index & -index
}
}
执行结果如下: