zoukankan      html  css  js  c++  java
  • 51nod 1510 最小化序列 | DP 贪心

    题目描述

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

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

    ∑|A[i]−A[i+k]|

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

    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


     

    题解

    这道题相当于把所有数分成了互不关联的k组,由于n不一定是k的倍数,其中一些组有 n / k + 1个元素,另一些有 n / k 个元素。

    在每一组中,为了使“相邻元素的差的绝对值之和”最小,将元素从小到大排序,则这一组的“相邻元素的差的绝对值之和”就是最大元素-最小元素。那么只要使每一组的最大值-最小值最小就好了。

    很容易想到把整个数组排好序后,直接取前n/k + 1组为第一组,取下面n/k + 1组为第二组……在n是k的倍数时这很好,可一个问题是:有些组有n/k个元素,有些有n/k+1个元素,令哪些组为前者,哪些为后者呢?这会影响最终的答案。

    发现两种“组”的数目是固定的,并且都小于等于5000,那么我们结合dp:

    dp[i][j]表示n/k + 1个元素的组已经选了i个,n/k个元素的组已经选了j个,能得到的最小分数。

    dp[i][j] 可以从 dp[i - 1][j] 和 dp[i][j - 1]两个转移。

    设排序后的序列为a[i], p1表示dp[i - 1][j]在排序后的序列中一共用完了前多少个元素,p2表示dp[i][j - 1]在排序后的序列中一共用完了前多少个元素。

    那么可以写出状态转移方程:

    dp[i][j] = min(dp[i - 1][j] + a[p1 + (n/k + 1)] - a[p1 + k], dp[i][j - 1] + a[p2 + n/k] - a[p2])

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    #define INF 0x3f3f3f3f
    #define space putchar(' ')
    #define enter putchar('
    ')
    template <class T>
    bool read(T &x){
        char c;
        bool minus = 0;
        while(c = getchar(), c < '0' || c > '9')
        if(c == '-') minus = 1;
        else if(c == EOF) return 0;
        x = c - '0';
        while(c = getchar(), c >= '0' && c <= '9')
        x = x * 10 + c - '0';
        if(minus) x = -x;
        return 1;
    }
    template <class T>
    void write(T x){
        if(x < 0) putchar('-'), x = -x;
        if(x >= 10) write(x / 10);
        putchar('0' + x % 10);
    }
    
    const int N = 300005, M = 5005;
    int n, k, l1, l2, t1, t2, a[N], dp[M][M];
    
    int main(){
    
        read(n), read(k);
        for(int i = 1; i <= n; i++)
        read(a[i]);
        sort(a + 1, a + n + 1);
        l1 = n/k + 1, l2 = n/k;
        t1 = n % k, t2 = k - t1;
        for(int i = 1, p = 0; i <= t1; i++)
        dp[i][0] = dp[i - 1][0] + a[p + l1] - a[p + 1], p += l1;
        for(int j = 1, p = 0; j <= t2; j++)
        dp[0][j] = dp[0][j - 1] + a[p + l2] - a[p + 1], p += l2;
        for(int i = 1, p1, p2; i <= t1; i++)
        for(int j = 1; j <= t2; j++){
            p1 = (i - 1) * l1 + j * l2;
            p2 = i * l1 + (j - 1) * l2;
            dp[i][j] = min(dp[i - 1][j] + a[p1 + l1] - a[p1 + 1],
                   dp[i][j - 1] + a[p2 + l2] - a[p2 + 1]);
        }
        write(dp[t1][t2]), enter;
        
        return 0;
    }
  • 相关阅读:
    强化学习 | D3QN原理及代码实现
    Airtest入门及多设备管理总结
    JS图片base64压缩
    ABP框架
    .net gof23种设计模式
    VS2013添加Socket
    VS2013用InstallShield打包winfrom项目
    .net core3.1 log4net无法写日志
    git commit 修改提交说明信息
    screen 使用总结
  • 原文地址:https://www.cnblogs.com/RabbitHu/p/51nod1510.html
Copyright © 2011-2022 走看看