CF#317D dp

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <stack>
#include <set>
#include <map>
#include <vector>

using namespace std;
#define INF 0x2fffffff
#define LL long long
#define MAX(a,b) ((a)>(b))?(a):(b)
#define MIN(a,b) ((a)<(b))?(a):(b)
LL dp[5005][5005];
LL sum[300005];
int a[300005];

int main(){
    int n,k;
    cin >>  n >> k;
    for(int i = 1;i <= n;i++){
        scanf("%d",&a[i]);
    } 
    sort(a+1,a+n+1);
    sum[0] = 0;
    a[0] = a[1];
    for(int i = 1;i <= n;i++){
        sum[i] = sum[i-1] + a[i] - a[i-1];
    }   
    //分成k组
    //有n%k组是长度是n/k+1的,k-n%k组是长度为n/k的 
    int l1 = n/k+1;
    int l2 = n/k;
    int n1 = n%k;
    int n2 = k-n%k;
    for(int i = 0;i <= n1;i++){
        for(int j = 0;j <= n2;j++){
            if(i ==0 || j ==0){
                if(i == 0 && j == 0)
                    dp[i][j] = 0;
                else
                    if(i == 0)
                        dp[i][j] = dp[i][j-1] + sum[j*l2] - sum[(j-1)*l2+1];
                    else
                        dp[i][j] = dp[i-1][j] + sum[i*l1] - sum[(i-1)*l1+1];
                }
            }
            else 
                dp[i][j] = min(dp[i-1][j]+sum[i*l1+j*l2] - sum[(i-1)*l1 +j*l2+1],dp[i][j-1]+sum[i*l1+j*l2]-sum[i*l1+(j-1)*l2+1]);
        }
    }
    cout << dp[n1][n2] << endl;
    return 0; 
}

这是一道很神奇的dp,首先你要找到这个问题的状态。
很明显这个问题的状态不明显,他问题的是最小的和值,这个问题很容易想到是dp,如果一开始就把n,k做状态的映射,首先这个数组开不了,然后这样做并没有意思,因为找不到相关的子问题,n变化和k变化似乎并没有什么关系,找不到相关子问题可以解决原问题,这时候要意识到其实n和k之间能决定另外一种更加鲜明的变量,那就是这里面长度为n/k和长度为n/k+1的序列的长度,设一个为i另外一个为j,这时候如果设dp[i][j]就能能找到可以推出dp[i][j] 的子问题的dp[i-1][j] dp[i][j-1] 这两个子状态可以转移到
dp[i][j] 这个状态去

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值