51nod1510 最小化序列

1 篇文章 0 订阅

Description

现在有一个长度为n的数组A,另外还有一个整数k。数组下标从1开始。

现在你需要把数组的顺序重新排列一下使得下面这个的式子的值尽可能小。


特别的,你也可以不对数组进行重新排列。


Input

单组测试数据。
第一行包含两个整数n,k (2≤n≤3*10^5, 1≤k≤min(5000,n-1))。
第二行包含n个整数 A[1],A[2],...,A[n] (-10^9≤A[i]≤10^9)。

Output

输出答案占一行。

Input示例

3 2
1 2 4

Output示例

1

解题思路:(贪心+DP)

这题一看题就是把数组分成了k组,k组分别怎么排列后求,然后求k组之和最小,k组里面有n%k组有n/k + 1 个数,有k - n %k 个有n/k个,一个组内部怎样最小当然是按从小到大排最小,内部可以消掉,之等于最大值减最小值,所以所有的数排序,求怎样划分成k组能使和最小。dp方程(i表示有n/k个数):dp[i][j] = min(dp[i-1][j] + a[i * (n/k) + j * (n/k + 1) -1] - a[(i-1) * (n/k) + j * (n/k + 1) -1],dp[i][j-1]+a[i * (n/k) + j * (n/k + 1) -1] - a[i * (n/k) + (j -1)* (n/k + 1) -1],);

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 300001
#define INF 1<<30
using namespace std;

int dp[5001][5001];
int a[MAXN];

int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i = 0; i < n; i++)
        scanf("%d",a+i);
    sort(a,a+n);
    int x1 =  k - n % k;
    int x2 = n % k;
    k = n/k;
    dp[0][0] = 0;
    for(int i = 1; i <= x1; i++)
        dp[i][0] = dp[i-1][0] + a[i* k - 1] - a[(i-1) * k];
    for(int j = 1; j <= x2; j++)
        dp[0][j] = dp[0][j-1] + a[j * (k+1) -1 ] - a[ (j-1) * (k + 1)];
    for(int i = 1; i <= x1; i++)
        for(int j = 1; j <= x2; j++)
        {
            int k0 = (i-1) * k + j *( k + 1);
            int k1 = i * k  + (j - 1)* ( k + 1 );
            dp[i][j] = min((dp[i-1][j] + ((k0 + k) > n ? 0 :(a[k0 + k - 1] - a[k0]))),
                           (dp[i][j - 1] + ((k1 + k + 1) > n ? 0 : (a[k1 + k] - a[k1]))));
        }
    printf("%d\n",dp[x1][x2]);
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值