把n个数分成m段,每段的值为(MAX - MIN)2,求所能划分得到的最小值。
依然是先从小到大排个序,定义状态d(j, i)表示把前i个数划分成j段,所得到的最小值,则有状态转移方程:
d(j, i) = min { d(j-1, k) + (ai - ak+1)2 | 0 ≤ k < i }
设 l < k < i,且由k转移得到的状态比由l转移得到的状态更优。
有不等式:
整理成斜率形式:
后面的就可以相当于套模板了,不过这里要用滚动数组优化一下空间。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 7 const int maxn = 10000 + 10; 8 const int maxm = 5000 + 10; 9 const int INF = 0x3f3f3f3f; 10 11 int n, m; 12 13 int a[maxn]; 14 int d[2][maxn]; 15 16 int head, tail; 17 int Q[maxn]; 18 19 int cur; 20 21 int inline Y(int x) { return d[cur^1][x] + a[x+1] * a[x+1]; } 22 23 int inline DY(int p, int q) { return Y(q) - Y(p); } 24 25 int inline DX(int p, int q) { return a[q+1] - a[p+1]; } 26 27 int main() 28 { 29 freopen("in.txt", "r", stdin); 30 31 int T; scanf("%d", &T); 32 for(int kase = 1; kase <= T; kase++) 33 { 34 scanf("%d%d", &n, &m); 35 for(int i = 1; i <= n; i++) scanf("%d", a + i); 36 sort(a + 1, a + 1 + n); 37 38 memset(d[0], 0x3f, sizeof(d[0])); 39 d[0][0] = 0; 40 cur = 0; 41 for(int i = 1; i <= m; i++) 42 { 43 cur ^= 1; 44 head = tail = 0; 45 Q[tail++] = 0; 46 for(int j = 1; j <= n; j++) 47 { 48 while(head + 1 < tail && DY(Q[head], Q[head+1]) <= DX(Q[head], Q[head+1]) * 2 * a[j]) head++; 49 while(head + 1 < tail && DY(Q[tail-1], j) * DX(Q[tail-2], Q[tail-1]) <= DY(Q[tail-2], Q[tail-1]) * DX(Q[tail-1], j)) tail--; 50 Q[tail++] = j; 51 d[cur][j] = d[cur^1][Q[head]] + (a[j]-a[Q[head]+1]) * (a[j]-a[Q[head]+1]); 52 } 53 } 54 printf("Case %d: %d\n", kase, d[cur][n]); 55 } 56 57 return 0; 58 }
代码君
时间: 2024-10-21 05:03:35