在线评测链接:P1084
在一个遥远的王国里,有一座高耸入云的宝塔,据说里面藏有神秘的宝藏。但是,进入宝塔的道路异常困难,需要经过各种险阻,其中一个重要的关卡是“平衡之门”。
平衡之门是一条走廊,两边是无数个数码显示屏幕,每个屏幕上都显示着一个整数。在走廊中央有一个控制台,上面有两个按钮,一个是“乘2”按钮,一个是“除以 2 2 2”按钮。每次按下其中一个按钮,控制台上的数字就会相应地变化。走廊中的每个数字最多只能被操作一次。
王国的宝藏猎人们想要通过平衡之门,以最小的代价进入宝塔。他们发现,只需要选择恰好 k k k 个数字,使得每个数字经过不超过一次的操作后,可以得到所有的数字,就可以顺利通过平衡之门。现在他们需要你的帮助,计算出最小的极差,即经过操作后最大值和最小值之差的最小值。
第一行输入两个正整数 n n n 和 k k k ,代表数组长度以及选择的元素数量。
第二行输入 n n n个元素,代表给定的数组。
1 ≤ k ≤ n ≤ 1 0 5 1 \leq k \leq n \leq 10^5 1≤k≤n≤105
1 ≤ a i ≤ 1 0 9 1 \leq a_i \leq 10^9 1≤ai≤109
k k k 次操作后,数组极差的最小值。
输入输出示例仅供调试,后台判题数据一般不包含示例
输入
4 2
1 4 3 5
输出
2
输入
6 1
9 4 3 4 2 5
输出
3
1.观察到每个数最多修改一次,那么显然最优的修改方案一定是前 i i i小都乘以 2 2 2,前 k − i k-i k−i大都除以 2 2 2。
2.那么排序,枚举 i i i然后模拟这个过程,去求最大值最小值,复杂度达到 O ( n 2 ) O(n^2) O(n2) 。但好在我们只关心极差,即最大值和最小值,所以不需要去模拟操作每一个数。而是考虑一次操作后结果会分成三段,分别去求这三段的最值就好了。如下图所示,我们只需要从这六个红圈中取极值即可。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VdvFIyQ5-1687342006273)(/mt/P1084/img1.png)]
推荐几道贪心题~
LeetCode上的贪心题,代码随想录总结的非常好了,见 贪心 - 代码随想录
P1075 拼多多-2023.3.12-飞机大战
P1176 2023.04.08-华为od-第三题-最多等和不相交连续子序列
P1211 塔子大厂真题模拟赛-第一题-魔法石(Ⅰ)
P1070 2023.3.7-百度-第一题-最小化k序列平均值和
#include
using namespace std;
int in() {
int x;
cin >> x;
return x;
}
int main()
{
int n = in(), k = in();
int a[n];
for (int i = 0; i < n; i++) {
a[i] = in();
}
sort(a, a+n); //排序
int ans = (int)1e9;
for (int i = 0; i <= k; i++) { //枚举修改前i小
//三者不一定都存在,所以最小值初始化为一个较大的值,最大值初始化为一个小的值,再用三者更新
int mi = (int)1e9, mx = 0;
if(i > 0) { // 第一段的端点
mi = min(mi, a[0]*2);
mx = max(mx, a[i-1]*2);
}
if(k-i > 0) { // 第三段的端点
mi = min(mi, a[n-(k-i)]/2);
mx = max(mx, a[n-1]/2);
}
if(n != k) { // 第二段的端点
mi = min(mi, a[i]);
mx = max(mx, a[n-(k-i)-1]);
}
ans = min(ans, mx - mi); //更新极差最小值
}
cout << ans << endl;
}
n, k = map(int, input().split())
a = list(map(int, input().split()))
a.sort() # 排序
ans = float("inf")
for i in range(k + 1): # 枚举修改前i小
# 三者不一定都存在,所以最小值初始化为一个较大的值,最大值初始化为一个小的值,再用三者更新
mi, mx = float("inf"), 0
if i > 0: # 第一段的端点
mi = min(mi, a[0] * 2)
mx = max(mx, a[i - 1] * 2)
if k - i > 0: # 第三段的端点
mi = min(mi, a[n - (k - i)] // 2)
mx = max(mx, a[n - 1] // 2)
if n != k: # 第二段的端点
mi = min(mi, a[i])
mx = max(mx, a[n - (k - i) - 1])
ans = min(ans, mx - mi) # 更新极差最小值
print(ans)
import java.util.*;
class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt(), k = in.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = in.nextInt();
}
Arrays.sort(a); //排序
int ans = (int)1e9;
for (int i = 0; i <= k; i++) {
int mi = (int)1e9, mx = 0;
// 第一段的端点
if(i > 0) {
mi = Math.min(mi, a[0]*2);
mx = Math.max(mx, a[i-1]*2);
}
// 第三段的端点
if(k-i > 0) {
mi = Math.min(mi, a[n-(k-i)]/2);
mx = Math.max(mx, a[n-1]/2);
}
// 第二段的端点
if(n != k) {
mi = Math.min(mi, a[i]);
mx = Math.max(mx, a[n-(k-i)-1]);
}
// 更新最小极差
ans = Math.min(ans, mx - mi);
}
System.out.println(ans);
}
}
package main
import (
"fmt"
"sort"
)
func main() {
var n, k int
fmt.Scan(&n, &k)
// 读入序列并排序
a := make([]int, n)
for i := 0; i < n; i++ {
fmt.Scan(&a[i])
}
sort.Ints(a)
// 初始化最小极差为一个较大的值
ans := int(1e9)
// 枚举修改前i小的数
for i := 0; i <= k; i++ {
// 初始化三个段的端点的最小值和最大值
mi, mx := int(1e9), 0
// 第一段的端点
if i > 0 {
mi = min(mi, a[0]*2)
mx = max(mx, a[i-1]*2)
}
// 第三段的端点
if k-i > 0 {
mi = min(mi, a[n-(k-i)]/2)
mx = max(mx, a[n-1]/2)
}
// 第二段的端点
if n != k {
mi = min(mi, a[i])
mx = max(mx, a[n-(k-i)-1])
}
// 更新最小极差
ans = min(ans, mx-mi)
}
// 输出最小极差
fmt.Println(ans)
}
// 取两个整数的最小值
func min(a, b int) int {
if a < b {
return a
}
return b
}
// 取两个整数的最大值
func max(a, b int) int {
if a > b {
return a
}
return b
}
process.stdin.resume();
process.stdin.setEncoding('utf-8');
let input = '';
process.stdin.on('data', (data) => {
input += data;
return;
});
process.stdin.on('end', () => {
const lines = input.trim().split('\n');
const nk = lines[0].trim().split(' ').map(Number);
const n = nk[0], k = nk[1];
const a = lines[1].trim().split(' ').map(Number);
a.sort((a , b) => {return a - b;}); // 排序
let ans = 1e9;
for (let i = 0; i <= k; i++) { // 枚举修改前i小
// 三者不一定都存在,所以最小值初始化为一个较大的值,最大值初始化为一个小的值,再用三者更新
let mi = 1e9, mx = 0;
if (i > 0) { // 第一段的端点
mi = Math.min(mi, a[0] * 2);
mx = Math.max(mx, a[i - 1] * 2);
}
if (k - i > 0) { // 第三段的端点
mi = Math.min(mi, a[n - (k - i)] >> 1);
mx = Math.max(mx, a[n - 1] >> 1);
}
if (n !== k) { // 第二段的端点
mi = Math.min(mi, a[i]);
mx = Math.max(mx, a[n - (k - i) - 1]);
}
ans = Math.min(ans, mx - mi); // 更新极差最小值
}
console.log(ans);
});