DP-牛客寒假集训营2- 施魔法

DP-牛客寒假集训营2- 施魔法

题目:
在这里插入图片描述
题意:
输 入 n , k 。 输入n,k。 nk
输 入 n 个 数 a 1 , a 2 , . . . , a n 。 输入n个数a_1,a_2,...,a_n。 na1,a2,...,an
将 这 n 个 数 分 成 几 个 集 合 , 每 个 集 合 中 至 少 包 含 k 个 元 素 。 将这n个数分成几个集合,每个集合中至少包含k个元素。 nk
每 个 集 合 S 的 代 价 为 m a x { a i − a j } , ( a i , a j ∈ S ) 。 每个集合S的代价为max \{a_i-a_j\},(a_i,a_j∈S)。 Smax{aiaj},(ai,ajS)
求 所 有 集 合 代 价 总 和 的 最 小 值 。 求所有集合代价总和的最小值。

题解:
要 使 得 总 代 价 最 小 , 那 么 每 个 集 合 中 最 大 值 与 最 小 值 的 差 就 要 尽 量 小 。 要使得总代价最小,那么每个集合中最大值与最小值的差就要尽量小。 使

容 易 证 明 最 优 解 必 然 是 在 连 续 有 序 的 区 间 中 取 得 的 , 因 此 首 先 将 n 个 数 排 序 。 容易证明最优解必然是在连续有序的区间中取得的,因此首先将n个数排序。 n

在 前 i 个 元 素 中 得 到 的 最 小 代 价 与 在 前 i − 1 个 元 素 中 得 到 的 最 小 代 价 是 相 关 的 。 在前i个元素中得到的最小代价与在前i-1个元素中得到的最小代价是相关的。 ii1

我 们 设 d p [ i ] 代 表 考 虑 前 i 个 元 素 的 最 小 代 价 , 显 然 当 i < k 时 , d p [ i ] 是 没 有 意 义 的 。 我们设dp[i]代表考虑前i个元素的最小代价,显然当i<k时,dp[i]是没有意义的。 dp[i]i,i<k,dp[i]

则 d p [ i ] = m i n ( d p [ j − 1 ] + a [ i ] − a [ j ] ) = m i n ( d p [ j − 1 ] − a [ j ] ) + a [ i ] , ( j ∈ [ 1 , i − k + 1 ] ) 。 则dp[i]=min(dp[j-1]+a[i]-a[j])=min(dp[j-1]-a[j])+a[i],(j∈[1,i-k+1])。 dp[i]=min(dp[j1]+a[i]a[j])=min(dp[j1]a[j])+a[i],(j[1,ik+1])

即 考 虑 区 间 [ 1 , i ] 的 元 素 的 最 小 代 价 = m i n ( 区 间 [ 0 , j − 1 ] 的 元 素 的 最 小 代 价 + 区 间 [ j , i ] 的 代 价 , 即 最 大 值 a [ i ] − 最 小 值 a [ j ] ) , ( j ∈ [ 1 , i − k + 1 ] ) 。 即考虑区间[1,i]的元素的最小代价=min(区间[0,j-1]的元素的最小代价+区间[j,i]的代价,即最大值a[i]-最小值a[j]) ,(j∈[1,i-k+1])。 [1,i]=min([0,j1]+[j,i]a[i]a[j]),(j[1,ik+1])

最 后 等 价 于 求 m i n ( d p [ j − 1 ] − a [ j ] ) , ( j ∈ [ 1 , i − k + 1 ] ) , 即 d p [ j − 1 ] − a [ j ] 前 缀 和 的 最 小 值 最后等价于求min(dp[j-1]-a[j]),(j∈[1,i-k+1]),即dp[j-1]-a[j]前缀和的最小值 min(dp[j1]a[j]),(j[1,ik+1])dp[j1]a[j]

代码:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x7fffffff
using namespace std;
const ll N=3e5+5;
int n,k;
ll a[N],dp[N];//  dp[i]:使用前i个元素的最小代价
ll pre;
//dp[i]=min(dp[j-1]+a[i]-a[j])=min(dp[j-1]-a[j])+a[i]
//     j∈[1,i-k+1]
int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);

    sort(a+1,a+n+1);

    for(int i=1;i<k;i++) dp[i]=inf;//前k-1项无法满足题意,初始化为inf
    pre=dp[0]-a[1];  //pre即dp[i-1]-a[i]前缀最小值
    
    for(int i=k;i<=n;i++)
    {
        dp[i]=a[i]+pre; //
        pre=min(pre,dp[i-k+1]-a[i-k+2]);
    }
    cout<<dp[n]<<endl;

    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值