HDU 3480 - Division - [斜率DP]

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3480

Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 999999/400000 K (Java/Others)

Little D is really interested in the theorem of sets recently. There’s a problem that confused him a long time.   
Let T be a set of integers. Let the MIN be the minimum integer in T and MAX be the maximum, then the cost of set T if defined as (MAX – MIN)^2. Now given an integer set S, we want to find out M subsets S1, S2, …, SM of S, such that 
and the total cost of each subset is minimal.

Input

The input contains multiple test cases. 
In the first line of the input there’s an integer T which is the number of test cases. Then the description of T test cases will be given. 
For any test case, the first line contains two integers N (≤ 10,000) and M (≤ 5,000). N is the number of elements in S (may be duplicated). M is the number of subsets that we want to get. In the next line, there will be N integers giving set S. 

Output

For each test case, output one line containing exactly one integer, the minimal total cost. Take a look at the sample output for format. 

Sample Input

2
3 2
1 2 4
4 2
4 7 10 1

Sample Output

Case 1: 1
Case 2: 18

 

题意:

给出含有N元素的集合S,选取M个S的子集,要求满足SU S2 U … U SM = S;

定义一个集合的最大元素为MAX,最小元素为MIN,它的花费为(MAX - MIN)2,现要求所有子集的总花费最少为多少。

 

题解:

先将S内元素从小到大排列,然后将这N个元素的序列分成M组(因为若有重叠元素,必然会使得花费增加);

那么假设dp[i][j]为前i个数分成j组的最小花费,那么求出dp[N][M]即可回答问题;

状态转移方程为dp[i][j] = min{ dp[k][j-1] + (S[i] - S[k+1])2 },j-1≤k<i;

那么当j固定时,计算dp[i][j]时需要枚举k,若k可能取值到a,b两点,且j-1≤a<b<i,

若有 dp[b][j-1] + (S[i] - S[b+1])2 ≤ dp[a][j-1] + (S[i] - S[a+1])2,则b点优于a点;

将上式变形,得到:

b点优于a点 <=> 

再然后就是斜率优化的老套路了(斜率优化的详情查看斜率DP分类里之前的文章),就不再赘述。

 

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=10000+5;

int n,m,S[maxn];
int dp[maxn][maxn];
int q[maxn],head,tail;

int up(int a,int b,int j) //g(a,b)的分子部分
{
    return (dp[b][j-1]+S[b+1]*S[b+1])-(dp[a][j-1]+S[a+1]*S[a+1]);
}
int down(int a,int b) //g(a,b)的分母部分
{
    return 2*S[b+1]-2*S[a+1];
}

int main()
{
    int t;
    scanf("%d",&t);
    for(int kase=1;kase<=t;kase++)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d",&S[i]);
        sort(S+1,S+n+1);

        for(int i=1;i<=n;i++) dp[i][1]=(S[i]-S[1])*(S[i]-S[1]);
        for(int j=2;j<=m;j++)
        {
            head=tail=0;
            q[tail++]=j-1;
            for(int i=j;i<=n;i++)
            {
                while(head+1<tail)
                {
                    int a=q[head], b=q[head+1];
                    if(up(a,b,j)<=S[i]*down(a,b)) head++; //g(a,b)<=S[i]
                    else break;
                }
                int k=q[head];
                dp[i][j]=dp[k][j-1]+(S[i]-S[k+1])*(S[i]-S[k+1]);

                while(head+1<tail)
                {
                    int a=q[tail-2], b=q[tail-1];
                    if(up(a,b,j)*down(b,i)>=up(b,i,j)*down(a,b)) tail--; //g(a,b)>=g(b,i)
                    else break;
                }
                q[tail++]=i;
            }
        }

        printf("Case %d: %d\n",kase,dp[n][m]);
    }
}

注意DP边界的初始化。

posted @ 2018-04-18 22:34  Dilthey  阅读(329)  评论(0编辑  收藏  举报