2020牛客寒假算法基础集训营2——H.施魔法【DP】

题目传送门


题目描述

牛可乐有 n 个元素( 编号 1…n ),第 i 个元素的能量值为 a i a_i
牛可乐可以选择至少k 个元素来施放一次魔法,魔法消耗的魔力是这些元素能量值的极差。形式化地,若所用元素编号集合为 S,则消耗的魔力为 max i S { a i } min i S { a i } \max_{i\in S}\{a_i\}-\min_{i\in S}\{a_i\}

牛可乐要求每个元素必须被使用恰好一次
牛可乐想知道他最少需要多少魔力才能用完所有元素,请你告诉他。


输入描述:

第一行两个正整数 n , k \text{}n, k

第二行 n 个整数 a 1 , a 2 , , a n a_1,a_2,\dots,a_n

保证 1 k n 3 × 1 0 5 1\leq k\leq n\leq 3\times 10^5 0 a i 1 0 9 0\leq a_i\leq 10^9


输出描述:

输出一行,一个整数表示答案。


输入

4 2
8 7 114514 114513


输出

2


说明

使用第 1、2 个元素施放一次魔法,消耗魔力为 8-7=1;第 3、4 个元素施放一次魔法,消耗魔力为 114514-114513=1;两个魔法一共消耗 2 点魔力。


题解

  • 先将元素按能量值排序,下文默认已排序。

  • 可以证明存在一个最优方案,满足每个魔法一定消耗一段连续的元素。

  • 注意是至少取 k k 段,那么从 k + 1 k+1 开始DP

  • 定义 d p [ i ] i dp[i]代表:前 i 项最优解 ,那么可以得到,对于任意位置:

    1. 是拓展前面,从长度 m m 变成 m + 1 m+1 ( m > = k ) (数学归纳法思想,任意位置肯定都是合法的,m>=k成立)
    2. 断开前面,从当前位置往前 k 1 k-1 项,和当前第 i i 项,组成长度为 k k 的序列。
  • O ( N K ) 时间复杂度 O(N-K)


#AC-Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 5;


ll a[maxn];
ll dp[maxn];
int main() {
	int n, k;	cin >> n >> k;
	for (int i = 1; i <= n; ++i)	dp[i] = LLONG_MAX, cin >> a[i];
	sort(a + 1, a + 1 + n);
	dp[k] = a[k] - a[1];
	for (int i = k + 1; i <= n; ++i)
		dp[i] = min(dp[i - 1] - a[i - 1] + a[i], dp[i - k] + a[i] - a[i - k + 1]);
	cout << dp[n] << endl;
}
发布了157 篇原创文章 · 获赞 99 · 访问量 9813

猜你喜欢

转载自blog.csdn.net/Q_1849805767/article/details/104204343