codeforces 571B B. Minimization(dp)

题目链接:

codeforces 571B


题目大意:

给出一个序列,可以任意调整序列的顺序,使得给出的式子的值最小

k=1nk|aiai+k|


题目分析:

  • 我们知道通过递增k可以得到一条链,那么我们就能够得到n%k条长度为n/k+1的链,得到k-n%k条长度为n/k的链,因为链上相邻的元素都会有一个差,这个差之和是 ana1 ,因为错项相消了。
  • 因为链不是相互连接的,所以我们需要减去断点处的值,总和一定,为了使最终的结果最小,我们只需要使断点处的值的和最大。
  • 定义状态dp[i][j]代表链1的个数是i和链2的个数是j的时候最大的断点处的值之和。
  • 我们提前将所有元素排好序,这样相邻元素的差最小。
  • 那么转移方程就是
    dp[i][j]=max(dp[i1][j]+a[k1+1]a[k1],k1=(i1)l1+jl2,dp[i][j1]+a[k2+1]a[k2],k2=il1+(j1)l2)


AC代码:

#include 
#include 
#include 
#include 
#define MAX 300007

using namespace std;

int n,k,a[MAX];
int dp[5007][5007];

int main ( )
{
    while ( ~scanf ( "%d%d" , &n , &k ) )
    {
        for ( int i = 1; i <= n ; i++ )
            scanf ( "%d" , &a[i] );
        sort ( a+1 , a+n+1 );
        int num1 = n%k;
        int len1 = n/k+1;
        int num2 = k - num1;
        int len2 = n/k;
        a[0] = a[1];
        memset ( dp , 0 , sizeof ( dp ) );
        for ( int i = 0 ; i <= num1 ; i++ )
            for ( int j = 0 ; j <= num2 ; j++ )
            {
                if ( i )
                {
                    int k = (i-1)*len1 + j*len2;
                    dp[i][j] = max ( dp[i][j] , dp[i-1][j] + a[k+1]-a[k] );
                }
                if ( j )
                {
                    int k = i*len1 + (j-1)*len2;
                    dp[i][j] = max ( dp[i][j] , dp[i][j-1] + a[k+1]-a[k] );
                }
            }
        int ans = a[n]-a[1] - dp[num1][num2];
        printf ( "%d\n" , ans );
    }
}

你可能感兴趣的:(codeforces的dp专题)