题目链接: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++可能会超时。
代码:
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<algorithm> 5 using namespace std; 6 typedef long long LL; 7 const int N=1e4+5; 8 9 int head,tail; 10 int dp[N][5005],a[N],q[N]; 11 12 int getUP(int m,int k,int j){ 13 return dp[k][j-1]+a[k+1]*a[k+1]-dp[m][j-1]-a[m+1]*a[m+1]; 14 } 15 16 int getDOWN(int m,int k){ 17 return 2*(a[k+1]-a[m+1]); 18 } 19 20 //dp[i][j]=min{dp[k][j-1]+(a[i]-a[k+1])^2} 21 int getDP(int i,int j,int k){ 22 return dp[k][j-1]+(a[i]-a[k+1])*(a[i]-a[k+1]); 23 } 24 25 int main(){ 26 int t,cas=0; 27 scanf("%d",&t); 28 int cnt=0; 29 while(t--){ 30 int n,m; 31 scanf("%d%d",&n,&m); 32 for(int i=1;i<=n;i++){ 33 scanf("%d",&a[i]); 34 } 35 sort(a+1,a+1+n); 36 for(int i=1;i<=n;i++){ 37 dp[i][1]=(a[i]-a[1])*(a[i]-a[1]); 38 } 39 40 for(int j=2;j<=m;j++){ 41 head=tail=0; 42 q[tail++]=j-1; 43 for(int i=j;i<=n;i++){ 44 cnt++; 45 while(head+1<tail&&getUP(q[head],q[head+1],j)<=a[i]*getDOWN(q[head],q[head+1])){ 46 head++; 47 } 48 dp[i][j]=getDP(i,j,q[head]); 49 while(head+1<tail&&getUP(q[tail-1],i,j)*getDOWN(q[tail-2],q[tail-1])<=getUP(q[tail-2],q[tail-1],j)*getDOWN(q[tail-1],i)){ 50 tail--; 51 } 52 q[tail++]=i; 53 } 54 } 55 printf("Case %d: %d\n",++cas,dp[n][m]); 56 } 57 return 0; 58 }
时间: 2024-10-03 14:57:39