【Codeforces 571B】Minimization | 思维dp

题目链接:https://codeforces.ml/contest/571/problem/B

题目大意:

给一段序列a_i,你可以对其重排,要求重排之后的:

ans = \sum_i^{n-k}{|a_i-a_{i+k}|}最大

题目思路:

首先裂项相消:

a[i+k]-a[i]

a[i+2*k] - a[i+k]

sum = a[i+x*k]-a[i]

性质1:也就是这个每个数相距为k的子序列的权值实际即为a[i+x*k]-a[i]

性质2:

可以发现有长度为n%k个 n/k+1的子序列

有k-n%k个 n/k的子序列

因为要求重排,所以直接排序后,题目就转换为:

扫描二维码关注公众号,回复: 11436904 查看本文章

选出n%k段连续区间长度为n/k+1的区间 和 k-n%k个区间长度为n/k的区间,使得每段区间的最大值减最小值的和最小

所以令dp[i][k] 选了i段n/k+1的区间,k段n/k的区间

所以转移方程:

令aim = i*(x+1) + k*x ,x = n/k

dp[i][k] = min(dp[i][k],dp[i-1][k] + num[aim]-num[aim-x])

dp[i][k] = min(dp[i][k],dp[i][k-1] + num[aim]-num[aim-x+1])

这种dp的状态转移方程并不常见,正确性是因为选了i段与k段可以把当前选择了多少个数给确定出来

这和之前牛客多校有一个状态转移非常的相似,不得不说还是要留心这种转移

Code:

/*** keep hungry and calm CoolGuang!***/
#include <bits/stdc++.h>
#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll INF=2e18;
const int maxn=1e6+6;
const int mod=1e9+7;
const double eps=1e-15;
inline bool read(ll &num)
{char in;bool IsN=false;
    in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,p;
ll dp[5005][5005];
ll num[maxn];
int main()
{
    read(n);read(m);
    ll x = n/m;
    ll len1 = n%m;///x+1
    ll len2 = m-n%m;///x
    for(int i=1;i<=n;i++) read(num[i]);
    sort(num+1,num+1+n);
    for(int i=0;i<=len1;i++){
        for(int k=0;k<=len2;k++){
            if(!i&&!k){
                dp[i][k] = 0;
                continue;
            }
            dp[i][k] = INF;
            int aim = i*(x+1)+k*x;
            if(i) dp[i][k] =min(dp[i][k],dp[i-1][k]+num[aim]-num[aim-x]);
            if(k) dp[i][k] =min(dp[i][k],dp[i][k-1]+num[aim]-num[aim-x+1]);
        }
    }
    printf("%lld\n",dp[len1][len2]);
    return 0;
}
/**
5 2 2
1 5
1 2
2 3
3 4
4 5
**/

猜你喜欢

转载自blog.csdn.net/qq_43857314/article/details/106997532
今日推荐