题意:给你一些数,把这些数分成M组,每组的花费是这组的 (max-min)^2。求最小花费
思路:
斜率优化+二维DP
f[i][m] 表示将前i个分作m个集合所得最小消费
首先应该排序,假设1,2,3,5,4 第四个数是5,花费一定比是4大。【贪心】
f[i][m] = min{f[j][m-1]+(a[i]-a[j+1])*(a[i]-a[j+1])}; 排序是为了让a[i]-a[j+1]总是最小的
若有j>k,且决策j优于决策k则有:
f[j][j-1]-f[k][j-1]+sq(a[j+1])-sq(a[k+1]) <= 2*(a[j+1]-a[k+1])*a[i]
先进行m循环枚举f[][m],每一层维护一个单调队列即可。
乘除耗费时间悬殊,如果直接除这个题就超时了。
代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 #define mem(a) memset(a,0,sizeof(a)) 5 #define mp(x,y) make_pair(x,y) 6 const int INF = 0x3f3f3f3f; 7 const ll INFLL = 0x3f3f3f3f3f3f3f3fLL; 8 inline ll read(){ 9 ll x=0,f=1;char ch=getchar(); 10 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 11 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 12 return x*f; 13 } 14 ////////////////////////////////////////////////////////////////////////// 15 const int maxn = 1e4+10; 16 int n,M; 17 int a[maxn],f[maxn][5005],q[maxn]; 18 int getup(int j,int k,int m){ 19 return f[j][m-1]-f[k][m-1]+a[j+1]*a[j+1]-a[k+1]*a[k+1]; 20 } 21 22 int getdown(int j,int k,int m){ 23 return 2*(a[j+1]-a[k+1]); 24 } 25 26 int main(){ 27 int T=read(); 28 for(int cas=1; cas<=T; cas++){ 29 n=read(),M=read(); 30 for(int i=1; i<=n; i++) 31 a[i]=read(); 32 sort(a+1,a+1+n); 33 for(int i=1; i<=n; i++) f[i][1] = (a[i]-a[1])*(a[i]-a[1]); 34 35 for(int m=2; m<=M; m++){ 36 int L=0,R=0; 37 for(int i=1; i<=n; i++){ 38 while(L<R && getup(q[L+1],q[L],m) <= a[i]*getdown(q[L+1],q[L],m)) L++; 39 int j = q[L]; 40 f[i][m] = f[j][m-1]+(a[i]-a[j+1])*(a[i]-a[j+1]); 41 while(L<R && getup(i,q[R],m)*getdown(q[R],q[R-1],m) <= getup(q[R],q[R-1],m)*getdown(i,q[R],m)) R--; 42 q[++R] = i; 43 } 44 } 45 46 cout << "Case " << cas << ": " << f[n][M] << endl; 47 } 48 49 return 0; 50 }