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