北京师范大学第十四届ACM决赛-重现赛 F:Training Plan(DP)

传送门

题意

将n个数分成m个集合,\(V_i表示max(x-y),x,y∈第\)i个集合,\(求minΣV_i\)

分析

我们先对难度排序,令dp[i][j]表示前i个数分成j个集合的最小费用
转移方程为

\[dp[i][j]=min(dp[k][j-1]+(a[i]-a[k+1])^2,dp[i][j]) \]

预处理dp[i][i],dp[i][1],开 long long

吐槽

开lld过,开I64d wa,用线段树可以达到\(O(n^2)\)
类似题目:Codeforces Round #426 (Div. 2)-The Bakery

代码

#include <bits/stdc++.h>
using namespace std;
 
#define ll long long
#define F(i,a,b) for(int i=a;i<=b;++i)
#define R(i,a,b) for(int i=a;i<b;++i)
#define mem(a,b) memset(a,b,sizeof(a))
 
int t;
int n,m,cnt;
ll a[505],b[505];
ll calc(int x)
{
    return 1LL*x*x;
}
ll dp[505][505];
int main()
{
    for(scanf("%d",&t);t--;)
    {
        scanf("%d %d",&n,&m);
        F(i,1,n) scanf("%I64d",a+i);
        sort(a+1,a+1+n);
        cnt=0;b[++cnt]=a[1];
        F(i,2,n)
        {
            if(a[i]!=a[i-1]) b[++cnt]=a[i];
        }
        if(cnt<=m) puts("0");
        else
        {
            F(i,0,cnt)F(j,0,m) dp[i][j]=1e18;
            F(j,1,m)F(i,j,m) dp[j][i]=0;
            F(i,2,n) dp[i][1]=calc(b[i]-b[1]);
            //printf("%lld\n",dp[3][1]);
            for(int i=1;i<=cnt;++i)for(int j=2;j<=m&&j<=i;++j)for(int k=j-1;k<i;++k)
            {
                //dp[i][j]=min(dp[i-1][j-1]);
                dp[i][j]=min(dp[k][j-1]+calc(b[i]-b[k+1]),dp[i][j]);
            }
            //printf("%lld\n",dp[3][1]);
            ll ans=1e18;
            F(i,1,m) ans=min(ans,dp[cnt][i]);
            printf("%lld\n",dp[cnt][m]);
        }
    }
    return 0;
}
posted @ 2017-09-30 18:05  遗风忘语  阅读(228)  评论(0编辑  收藏  举报