1. 问题描述:
有 n 张卡牌,编号 1∼n,每张卡牌的正面和背面都各有一个数字,第 i 张卡牌的正面数字为 ai,背面数字为 bi,初始时,所有卡牌都正面朝上,显示正面的数字,现在你可以将其中一些卡牌翻面,使其显示背面的数字,要求:
至少有 k 张卡牌保持正面朝上;
所有卡牌显示的数字之和尽可能小;
输出所有卡牌显示的数字之和的最小可能值;
输入格式
第一行包含两个整数 n,k,第二行包含 n 个整数 a1,a2,…,an,第三行包含 n 个整数 b1,b2,…,bn;
输出格式
一个整数,表示所有卡牌显示的数字之和的最小可能值。
数据范围
前 6 个测试点满足 1 ≤ n ≤ 10;
所有测试点满足 1 ≤ n ≤ 2 × 10 ^ 5,0 ≤ k ≤ n,1 ≤ ai,bi ≤ 10 ^ 4。
输入样例1:
3 1
5 4 6
3 1 5
输出样例1:
10
输入样例2:
5 3
3 4 7 10 3
4 5 5 12 5
输出样例2:
25
来源:https://www.acwing.com/problem/content/description/4400/
2. 思路分析:
分析题目可以知道正面显示数字的总和 s 是一定的,一开始所有卡牌都正面向上,我们最多可以翻转 n - k 张卡牌,使得最终卡牌显示的数字之和最小,可以发现每当我们翻转一张卡牌,那么 s 减少的是卡牌正面和反面数字的差值,所以我们肯定是先翻转那些差值贡献值大于 0 而且当前差值最大的卡牌,基于这个想法我们使用 vector 或者列表 q 记录下 每一个 a[i] - b[i] 的差值,最后对 q 从大到小排序,在 q 中最多找到 n - k 张贡献值大于 0 的卡牌,用 s 减去这些大于 0 的差值即可。
3. 代码如下:
python:
class Solution:
def process(self):
n, k = map(int, input().split())
# a 表示正面显示的数字, b 表示背面显示的数字, 正面显示数字的总和是一定的所以需要选择a[i]-b[i]差值最大的数字
a = list(map(int, input().split()))
b = list(map(int, input().split()))
# s 记录a中所有元素的总和
s = 0
q = list()
for i in range(n):
x = a[i]
s += x
# q记录a和b的差值
q.append(a[i] - b[i])
# 对 q 从大到小排序
q.sort(reverse=True)
# 我们至多选择n - k个差值最大的这些卡牌, 并且需要选择那些贡献值大于0的卡牌, 如果贡献值小于等于0还不如不翻牌
for i in range(n - k):
if q[i] > 0:
s -= q[i]
else:break
print(s)
if __name__ == '__main__':
Solution().process()
go:
package main
import (
"bufio"
"fmt"
"io"
"os"
"sort"
)
func run(r io.Reader, w io.Writer) {
// 使用bufio优化输入输出数据的读取和输出效率
in := bufio.NewReader(r)
out := bufio.NewWriter(w)
// 将缓存数据输出到标准输出中
defer out.Flush()
var (
n, k int
)
fmt.Fscan(in, &n, &k)
a := make([]int, n+10)
b := make([]int, n+10)
s := 0
q := make([]int, n+10)
for i := 0; i < n; i++ {
fmt.Fscan(in, &a[i])
s += a[i]
}
for i := 0; i < n; i++ {
fmt.Fscan(in, &b[i])
q = append(q, a[i]-b[i])
}
// 对b从大到小排序
sort.Sort(sort.Reverse(sort.IntSlice(q)))
for i := 0; i < n-k; i++ {
if q[i] > 0 {
s -= q[i]
} else {
break
}
}
fmt.Fprintln(out, s)
}
func main() {
run(os.Stdin, os.Stdout)
}