标签:
2 3 2 1 2 4 4 2 4 7 10 1
Case 1: 1 Case 2: 18
/*分析: 首先对于斜率dp我有个总结: 斜率dp一般应用于连续的一段或几段求最值 既1~k,k+1~j,j+1~...这样分段而不能跳开来求 仅仅有连续段才干用单调队列维护最值然后 dp[i]=dp[j]+(j+1~i)的值。 对于本题: 题目要求m个子数组的最值。而子数组中的元素不一定是原数组连续的 所以肯定不能直接用斜率优化,经过分析能够发现先进行从小到大排序 然后连续的m段最值就是能够求最值了。 所以:先对原数组进行从小到大排序 dp[i][j]表示以i结尾的j段的最值 从k+1~i作为一段 则:dp[i][j]=dp[k][j-1]+(s[i]-s[k+1])^2 如今就是怎样求到这个k使得dp[i][j]最小 如果k2<=k1<i 若:dp[k1][j-1]+(s[i]-s[k1+1])^2 <= dp[k2][j-1]+(s[i]-s[k2+1])^2 =>dp[k1][j-1]+s[k1+1]^2 - (dp[k2][j-1]+s[k2+1]^2) / (2s[k1+1]-2s[k2+1]) <= s[i] 所以: y1 = dp[k1][j-1]+s[k1+1]^2 x1 = 2s[k1+1] y2 = dp[k2][j-1]+s[k2+1]^2 x2 = 2s[k2+1] =>(y1 - y2)/(x1 - x2) <= i 单调队列维护下凸折线 */ #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <queue> #include <algorithm> #include <map> #include <cmath> #include <iomanip> #include <limits.h> #define INF 99999999 typedef long long LL; using namespace std; const int MAX = 10000+10; int n,m,index; int q[MAX]; int s[MAX],dp[2][MAX];//採用滚动数组 int GetY(int k1,int k2){ return dp[index^1][k1]+s[k1+1]*s[k1+1] - (dp[index^1][k2]+s[k2+1]*s[k2+1]); } int GetX(int k1,int k2){ return 2*(s[k1+1]-s[k2+1]); } int DP(){ int head=0,tail=1; index=0; for(int i=1;i<=n;++i)dp[index][i]=INF;//初始化 //dp[index][0]=0; for(int i=1;i<=m;++i){ index=index^1; head=tail=0; q[tail++]=0; for(int j=1;j<=n;++j){ //dp[index^1][0]=(i-1)*(s[j]-s[1])*(s[j]-s[1]); while(head+1<tail && GetY(q[head+1],q[head]) <= GetX(q[head+1],q[head])*s[j])++head; while(head+1<tail && GetY(j,q[tail-1])*GetX(q[tail-1],q[tail-2]) <= GetY(q[tail-1],q[tail-2])*GetX(j,q[tail-1]))--tail; q[tail++]=j; int k=q[head]; dp[index][j]=dp[index^1][k]+(s[j]-s[k+1])*(s[j]-s[k+1]); } } return dp[index][n]; } int main(){ int t,num=0; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i)scanf("%d",s+i); sort(s+1,s+1+n); printf("Case %d: %d\n",++num,DP()); } return 0; }
版权声明:本文博主原创文章。博客,未经同意不得转载。
标签:
原文地址:http://www.cnblogs.com/bhlsheji/p/4877840.html