技巧:
尺取 结果二分
思路
题目意思很坑,卡了半天没搞懂意思。 简单地说T次操作, 将A数组的所有子序列(连续的)每一个第K大的元素插入容器B ,然后再从B中找到第M大的数是多少。
首先 把问题转换(反推导 第M大说明每组子序列里面的K组成的B中, M的大小排行排行就是M, 也就是说有M-1组子序列 K值比M大)
假设B容器中第M大的元素是x。
然后通过尺取法验证假设值是否合理。 即如果满足第k大的子序列数量超过了M 那么说明这个假设X太小,反之X太大,需要正好等于
那怎么找到最合理的呢?
我们不停的将x在可行的范围里不断加大(结果二分尝试). 找到了那个最极限能够满足的seqcnt >= M的就是第M大的数 (其实最后一次就是等于)。
实现
package main
import (
"bufio"
. "fmt"
"io"
"os"
"strconv"
"strings"
)
func cvtstrArr(strArr []string) []int {
ans := make([]int, len(strArr))
for i := 0; i < len(strArr); i++ {
ans[i], _ = strconv.Atoi(strArr[i])
}
return ans
}
// 尺取法 --- 统计在x的情况下,满足第k大的子序列的数量。
func judge(arr []int, x, k, m int) bool {
seqcnt := 0 // 满足条件的子序列数量
gtecnt := 0 // 当前大于等于x的个数
j := 0 // 头位置
for i := 0; i < len(arr); i++ {
if arr[i] >= x {
gtecnt++
}
if gtecnt == k {
seqcnt += len(arr) - i
for arr[j] < x {
seqcnt += len(arr) - i
j++
}
gtecnt--
j++
}
}
return seqcnt >= m
}
// https://ac.nowcoder.com/acm/problem/14301
func NC14301(_r io.Reader, _w io.Writer) {
in, out := bufio.NewReader(_r), bufio.NewWriter(_w)
defer out.Flush()
cntStr, _ := in.ReadString('\n')
cntStr = strings.TrimSpace(cntStr)
cnt, _ := strconv.Atoi(cntStr)
for cnt > 0 {
firstLine, _ := in.ReadString('\n')
firstLine = strings.TrimSpace(firstLine)
firstArr := cvtstrArr(strings.Split(firstLine, " "))
secondLine, _ := in.ReadString('\n')
if firstArr[0] >= firstArr[1] {
secondLine = strings.TrimSpace(secondLine)
secondArr := cvtstrArr(strings.Split(secondLine, " "))
l, r := 0, int(^uint(0)>>1)
for l <= r {
x := l + (r-l)/2
if judge(secondArr, x, firstArr[1], firstArr[2]) {
l = x + 1
} else {
r = x - 1
}
}
Fprintln(out, l-1)
}
cnt--
}
}
func main() {
NC14301(os.Stdin, os.Stdout)
}

京公网安备 11010502036488号