HDU3480(斜率dp)

解决一个集合分割问题,目标是最小化每个子集最大值与最小值平方差之和。通过排序和动态规划方法实现,并利用斜率优化来提高效率。

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

这里写图片描述

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个元素的一个集合分成M个子集,使得每个子集的最大值与最小值的平方差的和最小
思路:很明显要先排序,排序完之后感觉就像是hdu2829
设 dp[i][j]表示前i个数分成j组的最小花费。
所以转移方程就是dp[i][j]=min{dp[k][j-1]+(num[i]-num[k+1])*(num[i]-num[k+1])}
然后斜率优化即可
AC代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 10010;
int num[maxn];
int dp[maxn][5005];
int N,M,head,tail;
int que[maxn];
int getfz(int i,int j,int k)
{
  return dp[i][k]-dp[j][k]+num[i+1]*num[i+1]-num[j+1]*num[j+1];
}
int getfm(int i,int j)
{
  return num[i+1]-num[j+1];
}
int main()
{
  int T,ncase=1;
  scanf("%d",&T);
  while(T--)
  {
    scanf("%d%d",&N,&M);
    for(int i=1;i<=N;i++)scanf("%d",&num[i]);
    sort(num+1,num+N+1);

    for(int i=1;i<=N;i++)dp[i][1]=(num[i]-num[1])*(num[i]-num[1]);
    for(int j=2;j<=M;j++)
    {
      head=tail=0;
      que[tail++]=j-1;
      for(int i=j;i<=N;i++)
      {
        while(head+1<tail&&getfz(que[head+1],que[head],j-1)<=2*num[i]*getfm(que[head+1],que[head]))head++;
        dp[i][j]=dp[que[head]][j-1]+(num[i]-num[que[head]+1])*(num[i]-num[que[head]+1]);
        while(head+1<tail&&getfz(i,que[tail-1],j-1)*getfm(que[tail-1],que[tail-2])<=getfz(que[tail-1],que[tail-2],j-1)*getfm(i,que[tail-1]))tail--;
        que[tail++]=i;
      }
    }
    printf("Case %d: %d\n",ncase++,dp[N][M]);
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值