1. 程式人生 > >HDU 3480 - Division - [斜率DP]

HDU 3480 - Division - [斜率DP]

total line form ble i++ a long 狀態 tail hat

題目鏈接: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的子集,要求滿足S1 U 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邊界的初始化。

HDU 3480 - Division - [斜率DP]