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边界的初始化。

原文地址:https://www.cnblogs.com/dilthey/p/8878094.html

时间: 2024-10-11 19:56:43

HDU 3480 - Division - [斜率DP]的相关文章

hdu 3480 Division (斜率优化)

Division Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 999999/400000 K (Java/Others) Total Submission(s): 2676    Accepted Submission(s): 1056 Problem Description Little D is really interested in the theorem of sets recently. There's a pro

HDU 3480 Division(斜率DP裸题)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3480 题目大意:将n个数字分成m段,每段价值为(该段最大值-该段最小值)^2,求最小的总价值. 解题思路:很单纯的斜率优化DP,得出状态转移方程:dp[i][j]=min{dp[k][j-1]+(a[i]-a[k+1])^2}(j-1<=k<i),然后斜率优化降到O(n^2)就好了. 注意:数据类型建议用int,不要用long long,后者乘法计算时间是前者的四倍,否则C++可能会超时. 代码:

HDU 3480 Division(斜率优化+二维DP)

Division Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 999999/400000 K (Java/Others) Total Submission(s): 3984    Accepted Submission(s): 1527 Problem Description Little D is really interested in the theorem of sets recently. There’s a pro

HDU 3480 Division DP + 四边形优化

水题,证明有单调性之后直接照着拍就好 #include <cstdio> #include <cstring> #include <algorithm> #include <climits> using namespace std; #define sq(x) ((x)*(x)) const int maxn = 10005; const int maxm = 5005; int f[maxn][maxm], s[maxn][maxm]; int val[m

HDU 3480 division

题目大意:一个有n个数的集合,现在要求将他分成m+1个子集,对子集i设si表示该集合中最大数与最小数的差的平方.求所有si的和的最小值.n<=10000,m<=5000. 分析:最优解的m个集合肯定不会相交,也不会出现空集,而且每个子集的数必定是连续的. 所以可以将n个数先排序,在来进行dp求解. f[i][j]表示前j个数分成i个集合的最优解. 转移方程为:f[i][j]=min(f[i-1][k]+(num[j]-num[k+1])^2 设决策点k1<k2,若有k2比k1更优,则有:

hdu 3480 Division(四边形不等式优化)

Problem Description 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

hdu 3507 斜率dp

不好理解,先多做几个再看 此题是很基础的斜率DP的入门题. 题意很清楚,就是输出序列a[n],每连续输出的费用是连续输出的数字和的平方加上常数M 让我们求这个费用的最小值. 设dp[i]表示输出前i个的最小费用,那么有如下的DP方程: dp[i]= min{ dp[j]+(sum[i]-sum[j])^2 +M }  0<j<i 其中 sum[i]表示数字的前i项和. 相信都能理解上面的方程. 直接求解上面的方程的话复杂度是O(n^2) 对于500000的规模显然是超时的.下面讲解下如何用斜率

Print Article hdu 3507 一道斜率优化DP 表示是基础题,但对我来说很难

Print Article Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)Total Submission(s): 4990    Accepted Submission(s): 1509 Problem Description Zero has an old printer that doesn't work well sometimes. As it is antique

HDU 2993 MAX Average Problem(斜率DP经典+输入输出外挂)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2993 题目大意:给出n,k,给定一个长度为n的序列,从其中找连续的长度大于等于k的子序列使得子序列中的平均值最小. 解题思路:斜率DP经典题, 详细分析见: NOI2004年周源的论文<浅谈数形结合思想在信息学竞赛中的应用> 还有要注意要用输入输出外挂,不是getchar()版的,是fread()版的,第一次遇到这么变态的题目- -|||. 代码: 1 #include<iostream&g