HDU 3045 DP 斜率优化 Picnic Cows

题意:将n个数分成若干组,每组数字的个数不少于t个,要把每组的数字减小到这组最小值,求所有数字减少的最小值。

先将这n个数从小到大排个序,可以想到一组里面的数一定是排序后相邻的。

设d(i)表示前i个数分完组以后减少的最小值,考虑j~i为一组,则有状态转移方程

还是一样的处理方法,设k < j ≤ i - t,且j~i为一组的值比k~i为一组的值更优。

则有不等式:

化简,把i分离出来,整理成斜率的形式:

写到这里就应该很清楚地能够看出来X和Y的表达式了。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 typedef long long LL;
 8 
 9 const int maxn = 400000 + 10;
10 
11 int n, t;
12 
13 LL a[maxn], sum[maxn];
14 LL d[maxn];
15 
16 int head, tail;
17 int Q[maxn];
18 
19 LL inline Y(int x) { return d[x-1] - sum[x-1] + a[x] * (x - 1); }
20 
21 LL inline DY(int p, int q) { return Y(q) - Y(p); }
22 
23 LL inline DX(int p, int q) { return a[q] - a[p]; }
24 
25 int main()
26 {
27     while(scanf("%d%d", &n, &t) == 2)
28     {
29         for(int i = 1; i <= n; i++) scanf("%I64d", a + i);
30         sort(a + 1, a + 1 + n);
31         for(int i = 1; i <= n; i++) sum[i] = sum[i-1] + a[i];
32 
33         memset(d, 0, sizeof(d));
34         for(int i = t; i < 2 * t && i <= n; i++) d[i] = sum[i] - a[1] * i;
35 
36         head = tail = 0;
37         Q[tail++] = 1;
38         for(int i = t * 2; i <= n; i++)
39         {
40             while(head + 1 < tail && DY(Q[tail-1], i-t+1) * DX(Q[tail-2], Q[tail-1]) <= DY(Q[tail-2], Q[tail-1]) * DX(Q[tail-1], i-t+1)) tail--;
41             Q[tail++] = i - t + 1;
42             while(head + 1 < tail && DY(Q[head], Q[head+1]) <= DX(Q[head], Q[head+1]) * i) head++;
43             d[i] = d[Q[head]-1] + sum[i] - sum[Q[head]-1] - (i-Q[head]+1) * a[Q[head]];
44         }
45 
46         printf("%I64d
", d[n]);
47     }
48 
49     return 0;
50 }
代码君
原文地址:https://www.cnblogs.com/AOQNRMGYXLMV/p/4693635.html