斜率优化DP
题意:
将含有N个元素的一个集合分成M个子集,使得每个子集的最大值与最小值平方差的和最小。
可以想到贪心将元素从小到大排序,很快可以得出dp[i][j]-前i个子集分j个元素的最小花费,
dp方程 dp[i][j]=dp[i-1][k]+(a[j]-a[k+1]);但是需要三重循环,时间复杂度接受不了,所以需要优化,这题斜率优化和四边形不等式都可以,先看斜率优化。
设k1<k2<j,如果k1比k2优,则有dp[i-1][k1]+(a[j]-a[k1+1])^2<dp[i-1][k2]+(a[j]-a[k2+1])^2;
化简得 ( dp[i-1][k1]+a[k1+1]^2-(dp[i-1][k2]+a[k2+1]^2) )/(a[k1+1]-a[k2+1])<=2a[j];
————————————————
版权声明:本文为CSDN博主「utobe67」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/tobewhatyouwanttobe/article/details/36209591
下面贴代码 代码自己写的
1 #include<cstdio> 2 #include<algorithm> 3 #include<string.h> 4 using namespace std; 5 const int maxn=1e4+10; 6 const int maxm=5e3+10; 7 int a[maxn]; 8 int dp[maxn][maxm]; //dp[i][j]:前i个数分为j个集合的最小值; 9 int q[maxn]; 10 int up(int x,int y,int base) 11 { 12 return dp[y][base]-dp[x][base]+a[y+1]*a[y+1]-a[x+1]*a[x+1]; 13 } 14 int down(int x,int y) 15 { 16 return a[y+1]-a[x+1]; 17 } 18 void init() 19 { 20 memset(dp,0,sizeof(dp)); //这波初始化好像没什么必要; 21 } 22 int main() 23 { 24 int T,i,j; 25 scanf("%d",&T); 26 int cot=1; 27 while(T--){ 28 init(); 29 int n,m; 30 scanf("%d%d",&n,&m); 31 for(i=1;i<=n;i++) scanf("%d",&a[i]); 32 printf("Case %d: ",cot); 33 cot++; 34 sort(a+1,a+1+n); //从小到大排序; 35 for(i=1;i<=n;i++) dp[i][1]=(a[i]-a[1])*(a[i]-a[1]); //一个集合的情况下先列举; 36 for(j=2;j<=m;j++){ 37 int head=0,tail=0; 38 q[++tail]=j-1; //一个区间要分出j个集合,在这里进行新一轮的状态转移的时候, 39 //新的状态是一个集合,那么旧的状态就是j-1个集合 40 //要组成j-1个集合,至少j-1个数,所以就把这个数先放进去; 41 for(i=j;i<=n;i++){ 42 while(head<tail){ 43 int t1=q[head]; 44 int t2=q[head+1]; 45 int temp=2*a[i]; 46 if(up(t1,t2,j-1)<=down(t1,t2)*temp) head++; 47 else break; 48 } 49 int t=q[head]; 50 dp[i][j]=dp[t][j-1]+(a[i]-a[t+1])*(a[i]-a[t+1]); 51 while(head<tail){ 52 int t1=q[tail-1]; 53 int t2=q[tail]; 54 if(up(t2,i,j-1)*down(t1,t2)<=up(t1,t2,j-1)*down(t2,i)) tail--; 55 else break; 56 } 57 tail++; 58 q[tail]=i; 59 } 60 } 61 printf("%d\n",dp[n][m]); 62 } 63 return 0; 64 }