DP斜率优化总结

寒假事情比较多,刚回来的一周都是聚会,外加自己不务正业了几天浪费了大半的时间,春节前后还是抽空学习了一下斜率优化DP。

理论基础见NOI2004年周源的论文《浅谈数形结合思想在信息学竞赛中的应用》,只看了前面的部分,后面单调队列对非DP问题的优化没有看

斜率优化其实就是把每个状态看上直角坐标系上离散的点抽象出x,y 表示斜率 (y2 - y1) / (x2 - x1) 于一个关系状态i个函数的关系,然后维护点见斜率的上凸性或者下凸性。具体的情况要看于i有关的函数的单调性。

说一些注意事项:

注意斜率尽量中乘法,不要中实数,如有必要全用long long,以免溢出。注意 x2 - x1可能小于0不等式的负号要改变(因为这个错了无数的题)

例题1: MAX Average Problem HDU 2993 此题有点卡常数,代码尽量多优化一些

论文的原题,维护斜率的下凸性,我一律采用的单调队列的方法,而没有用凸包的方法。


另外的两道

hdu 3480 Division

同样是斜率优化,据说也可以用四边形不等式优化,有待研究

此题非常简单,建议做初学者入门题目 

y=dp[i - 1][j] + a[j + 1] * a[j + 1];

x=a[j + 1];

维护斜率下凸性即可

HDU 2480
/*
* =====================================================================================
*
* Filename: sequence.cpp
*
* Description: dp with queue
*
* Version: 1.0
* Created: 2011年02月01日 15时17分16秒
* Revision: none
* Compiler: gcc
*
* Author: ronaflx
* Company: hit-acm-group
*
* =====================================================================================
*/
#include
<iostream>
#include
<cstring>
#include
<algorithm>
#include
<cstdio>
using namespace std;
const int M = 5001;
const int N = 10002;
int a[N], dp[M][N];
int n, m;
int q[N], head, tail;

int DP()
{
for(int i = 1;i <= n;i++)
dp[
1][i] = (a[i] - a[1]) * (a[i] - a[1]);
for(int i = 2;i <= m;i++)
{
head
= tail = 0;
q[tail
++] = i - 1;
for(int j = i;j <= n;j++)
{
while(head + 1 < tail)
{
int p1 = q[head], p2 = q[head + 1];
int x1 = a[p1 + 1], x2 = a[p2 + 1];
int y1 = dp[i - 1][p1] + x1 * x1, y2 = dp[i - 1][p2] + x2 * x2;
if(y2 - y1 < 2 * a[j] * (x2 - x1))
head
++;
else
break;
}
int k = q[head];
dp[i][j]
= dp[i - 1][k] + (a[j] - a[k + 1]) * (a[j] - a[k + 1]);
while(head + 1 < tail && j != n)
{
int p1 = q[tail - 2], p2 = q[tail - 1], p3 = j;
int x1 = a[p1 + 1], x2 = a[p2 + 1], x3 = a[p3 + 1];
int y1 = dp[i - 1][p1] + x1 * x1, y2 = dp[i - 1][p2] + x2 * x2, y3 = dp[i - 1][p3] + x3 * x3;
if((y3 - y2) * (x2 - x1) <= (y2 - y1) * (x3 - x2))
tail
--;
else
break;
}
q[tail
++] = j;
}
}
return dp[m][n];
}

int main()
{
int t;
scanf(
"%d", &t);
for(int j = 1;j <= t;j++)
{
scanf(
"%d %d", &n, &m);
for(int i = 1;i <= n;i++)
scanf(
"%d", &a[i]);
sort(a
+ 1,a + n + 1);
printf(
"Case %d: %d\n",j, DP());
}
return 0;
}
hdu 2829 Lawrence

此题比较特殊

虽然是求最小值,但是由于关于i的函数是单调递减的,x也是关于i递减的所以要维护上凸性。

貌似是吧,记忆模糊了……

y和x是见代码。状态转移方程的推倒过程还是需要一些耐心的,开始的时候推错了多了一个sf,但是无限的WA

自己完成,不是1A,加深了对斜率优化的理解。

据说也可以四边形不等式,有待学习

HDU 2829
/*
* =====================================================================================
*
* Filename: sequence.cpp
*
* Description: dp with queue
*
* Version: 1.0
* Created: 2011年02月01日 15时17分16秒
* Revision: none
* Compiler: gcc
*
* Author: ronaflx
* Company: hit-acm-group
*
* =====================================================================================
*/
#include
<iostream>
#include
<cstring>
#include
<algorithm>
#include
<cstdio>
using namespace std;
const int SIZE = 1001;
const int INF = 100000000;
int dp[SIZE][SIZE];
int n, m;
int f[SIZE], a[SIZE], sf[SIZE], s[SIZE];
int q[SIZE], head, tail;

void preprocess()
{
s[n]
= 0;
f[n]
= 0;
for (int i = n - 1; i >= 0; i--)
{
s[i]
= s[i + 1] + a[i];
f[i]
= a[i] * s[i + 1];
sf[i]
= sf[i + 1] + f[i];
}
for (int i = 1; i < n; i++)
dp[
0][i] = sf[0];
}

int DP()
{
for (int i = 1; i < n; i++)
dp[
1][i] = sf[0] - s[i] * (s[0] - s[i]);
for (int i = 2; i <= m; i++)
{
head
= tail = 0;
q[tail
++] = 1;
dp[i][
1] = sf[1];
for (int j = 2; j < n; j++)
{
while (head + 1 < tail)
{
int p1 = q[head], p2 = q[head + 1];
int y1 = dp[i - 1][p1], y2 = dp[i - 1][p2];
int x1 = s[p1], x2 = s[p2];
if (y1 - y2 >= s[j] * (x1 - x2))
head
++;
else
break;
}
int k = q[head];
dp[i][j]
= dp[i - 1][k] - s[j] * (s[k] - s[j]);
while (head + 1 < tail)
{
int p1 = q[tail - 2], p2 = q[tail - 1], p3 = j;
int y1 = dp[i - 1][p1], y2 = dp[i - 1][p2], y3 = dp[i - 1][p3];
int x1 = s[p1], x2 = s[p2], x3 = s[p3];
if ((y2 - y3) * (x1 - x2) > (y1 - y2) * (x2 - x3))
tail
--;
else
break;
}
q[tail
++] = j;
}
}
int ans = INF;
for(int i = 1;i < n;i++)
ans
= min(ans,dp[m][i]);
return ans;
}

int main()
{
while (scanf("%d %d", &n, &m) == 2 && (n + m))
{
for (int i = 0; i < n; i++)
scanf(
"%d", &a[i]);
preprocess();
printf(
"%d\n", DP());
}
return 0;
}
这两道题都体现的斜率优化得降维的作用。

HDU 3401 一道很不错的单调队列的题

决策一共有三种:

/*
* 决策1:维持前一天的不变
* //该特性决定了dp[i][j] 以i为变量j为常量的单调不减性
* 决策2:买stock
* 决策3:卖stock
* 用单调队列结合1性质的单调性进行优化
*/

完成了一次降维,再又转移过程的单调性又完成了一次降维。个人觉得是一道非常不错的题,但是由于一些初始化的失误WA了无数次。
要仔细注意初始化的上下界,非常重要!!
HDU 3401
#include <iostream>
#include
<cstring>
#include
<cstdio>
#include
<algorithm>
using namespace std;
const int DAY = 2010;
const int STOCK = 2010;
const int INF = 100000000;
int dp[DAY][STOCK];
int day, n, w;
int buyp[DAY], sellp[DAY], buymost[DAY], sellmost[DAY];
int q[DAY], head, tail;

void DP()
{
for (int i = 0; i <= day; i++)
fill(dp[i], dp[i]
+ n + 1, -INF);
//初始化从 1 到 w + 1这些天,因为这些天的状态只能从决策1中取最优
for (int i = 1; i <= w + 1; i++)
for (int j = 0; j <= buymost[i]; j++)
dp[i][j]
= max(dp[i - 1][j], -buyp[i] * j);
for (int i = 2; i <= day; i++)//一定要从2开始, 因为上面的循环上线时buymost[i],而不是n
{
for (int j = 0; j <= n; j++)//从0开始,0也是合法状态
dp[i][j] = max(dp[i][j], dp[i - 1][j]);
if(i <= w + 1)
continue;
//买的情况,枚举当前状态
head = tail = 0;
for (int j = 0; j <= n; j++)
{
int k;
if (tail != head)
k
= q[tail - 1];
while (head != tail && (dp[i - w - 1][k] + buyp[i] * k) < (dp[i - w - 1][j] + buyp[i] * j))
{
tail
--;
k
= q[tail - 1];
}
q[tail
++] = j;
while (head != tail && j - q[head] > buymost[i])
head
++;
k
= q[head];
dp[i][j]
= max(dp[i][j], dp[i - w - 1][k] - buyp[i] * (j - k));
}
//卖的情况,枚举当前状态
head = tail = 0;
for (int j = n; j >= 0; j--)
{
int k;
if(tail != head)
k
= q[tail - 1];
while (head != tail && dp[i - w - 1][k] + sellp[i] * k < dp[i - w - 1][j] + sellp[i] * j)
{
tail
--;
k
= q[tail - 1];
}
q[tail
++] = j;
while (head != tail && q[head] - j > sellmost[i])
head
++;
k
= q[head];
dp[i][j]
= max(dp[i][j], dp[i - w - 1][k] + sellp[i] * (k - j));
}
}
}

int find_ans()
{
int ans = 0;
for (int i = 0; i <= n; i++)
ans
= max(ans, dp[day][i]);
return ans;
}

int main()
{
int cases;
scanf(
"%d", &cases);
while (cases--)
{
scanf(
"%d %d %d", &day, &n, &w);
for (int i = 1; i <= day; i++)
scanf(
"%d %d %d %d", &buyp[i], &sellp[i], &buymost[i], &sellmost[i]);
DP();
printf(
"%d\n", find_ans());
}
return 0;
}
/*
* 每写一个循环都一定要严格注意上下界是否正确
* 注意是否所有的状态都完成了题目需要的初始化
* 有的时候宁可编码麻烦一些也不要低级错误犯错
*/

HDU 3530
一道用单调队列 O(n)就可以完成的题,RMQ的话是O(nlogn)但是常数小一些。
此一需要注意的是,不可以用二分枚举长度,因为,不满足二分的性质
例如如下数据
5 3 5
1 2 3 4 5
枚举长度 1 5 mid = 3
没有满足的,就会向下枚举,但是实际上答案为5,思路局限在二分中,浪费了时间呀……

代码

/*
* =====================================================================================
*
* Filename: substring.cpp
*
* Description: HDU 3530
*
* Version: 1.0
* Created: 2011年01月30日 22时40分05秒
* Revision: none
* Compiler: gcc
*
* Author: ronaflx
* Company: hit-acm-group
*
* =====================================================================================
*/
/*
* 注意:此题不可以二分枚举长度
* 性质想不符合 如果长度m不满足条件 ,m + 1一定不满足条件
* 例如:
* 5 3 5
* 1 2 3 4 5
* 3不满足,但是4和5都满足条件
*/
#include
<iostream>
#include
<cstring>
#include
<cstdio>
using namespace std;
const int SIZE = 100001;
int qmax[SIZE], hmax, tmax;
int qmin[SIZE], hmin, tmin;
int a[SIZE];
int n, now, m, k;
int main()
{
while(scanf("%d %d %d", &n, &m, &k) == 3)
{
hmax
= tmax = hmin = tmin = 0;
int ans = 0;
now
= 1;
for(int i = 1;i <= n;i++)
scanf(
"%d", &a[i]);
for(int i = 1;i <= n;i++)
{
while(hmax != tmax && a[qmax[tmax - 1]] <= a[i])
tmax
--;
qmax[tmax
++] = i;
while(hmin != tmin && a[qmin[tmin - 1]] >= a[i])
tmin
--;
qmin[tmin
++] = i;
while(a[qmax[hmax]] - a[qmin[hmin]] > k)
{
if(qmin[hmin] < qmax[hmax])
now
= qmin[hmin++] + 1;
else
now
= qmax[hmax++] + 1;
}
if(a[qmax[hmax]] - a[qmin[hmin]] >= m)
ans
= max(ans,i - now + 1);
}
printf(
"%d\n",ans);
}
return 0;
}

CEOI 2004的题
我的第一个斜率优化,回头看看这题,状态的设计,斜率的分析做的都不是很好,不过选了一道难题对于理解还后有很多好处的。
此题的状态设计才是经典,如果设计合理状态,接下来就容易许多了,DP还得练习,状态设计能力还不够。
论文上的原题 06年汤泽 《




从一类单调性问题看算法的优化》的原题


ceoi 2004


/*
* =====================================================================================
*
* Filename: sequence.cpp
*
* Description: dp with queue
*
* Version: 1.0
* Created: 2011年02月01日 15时17分16秒
* Revision: none
* Compiler: gcc
*
* Author: ronaflx
* Company: hit-acm-group
*
* =====================================================================================
*/
#include
<iostream>
#include
<cstring>
#include
<cstdio>
using namespace std;

const int SIZE = 20002;
const int INF = 2000000000;
long long w[SIZE], d[SIZE];//题目数据
long long ds[SIZE];//i的位置,开始算
long long dp[SIZE];//状态表示第二个伐木场建在i处的最小费用
long long sw[SIZE], sp[SIZE];//表示把第一个木头到第i个的总重量和运到第i个位置的总费用
long long bp[SIZE];//表示从i到n的木头运送到山角的伐木场的费用
int n;
int q[SIZE], head,tail;

void preprocess()
{
ds[
1] = 0;
for(int i = 2;i <= n + 1;i++)
ds[i]
= ds[i - 1] + d[i - 1];
sw[
0] = sp[0] = 0;
for(int i = 1;i <= n + 1;i++)
{
sw[i]
= sw[i - 1] + w[i];
sp[i]
= sp[i - 1] + sw[i - 1] * d[i - 1];
}
bp[n
+ 1] = 0;
for(int i = n;i >= 1;i--)
bp[i]
= bp[i + 1] + w[i] * (ds[n + 1] - ds[i]);
}

void DP()
{
fill(dp
+ 1,dp + n + 1, INF);
long long ans = INF;
head
= tail = 0;
q[tail
++] = 1;
ans
= min(ans,sp[n + 1] - sw[0] * (ds[1] - ds[0]) - sw[1] * (ds[n + 1] - ds[1]));
for(int i = 2;i <= n;i++)
{
/*
* bp[i + 1] = sp[n + 1] - sp[i] - sw[i] * (ds[n + 1] - ds[i]);
* dp[i] = min(dp[i],bp[i + 1] + sp[j] + sp[i] - sp[j] - sw[j] * (ds[i] - ds[j]));
* dp[i] = min(dp[i],sp[n + 1] - sw[j] * (ds[i] - ds[j]) - sw[i] * (ds[n + 1] - ds[i]));
* 对于使dp[i]最小的k,dp[i]j - dp[i]k >= 0 (j < k)
* (sw[j] * ds[j] - sw[k] * ds[k]) / (sw[j] - sw[k]) <= ds[i] <= ds[i + 1];
* 所以i + 1 的决策量不小于k;
* y = sw[j] * ds[j] x = sw[j];斜率优化
*/
while(head + 1 < tail)
{
long long y1 = sw[q[head]] * ds[q[head]],y2 = sw[q[head + 1]] * ds[q[head + 1]];
long long x1 = sw[q[head]],x2 = sw[q[head + 1]];
if((y2 - y1) <= ds[i] * (x2 - x1))
head
++;
else
break;
}
int k = q[head];
dp[i]
= sp[n + 1] - sw[k] * (ds[i] - ds[k]) - sw[i] * (ds[n + 1] - ds[i]);
ans
= min(ans,dp[i]);
while(head + 1 < tail)
{
long long y1 = sw[q[tail - 2]] * ds[q[tail - 2]],y2 = sw[q[tail - 1]] * ds[q[tail - 1]],y3 = sw[i] * ds[i];
long long x1 = sw[q[tail - 2]],x2 = sw[q[tail - 1]],x3 = sw[i];
if((y2 - y1) * (x3 - x2) >= (y3 - y2) * (x2 - x1))
tail
--;
else
break;
}
q[tail
++] = i;
}
printf(
"%lld\n",ans);
}

int main()
{
while(scanf("%d", &n) == 1)
{
for(int i = 1;i <= n;i++)
scanf(
"%lld %lld", &w[i], &d[i]);
preprocess();
DP();
}
return 0;
}




以上为这几天的收获,接下来是网络流的深入学习,ohmylove的网络流全集早已入手,刷之~,多读论文,发现NOI的论文真的是一个大宝藏。
感谢涛哥在暑假的时候发的论文全集继续学习
另外还有兼顾DP的轻度练习,主要练习状态的设计能力。争取在队伍里担当的更多一些。
开学后学习一些没有掌握的数据结构,重点在数据结构辅助其他内容的综合题目。
加油努力学习

4月3日update
NOI 2005
瑰丽华尔兹
dp[i][j][k] 表示第i段时间到i,j的滑动的最长距离
根据4个方向用单调队列优化,写的很搓,4xxxms
http://mail.bashu.cn:8080/BSoiOnline/showproblem?problem_id=2247


NOI 5002 瑰丽华尔兹


1 /*
2 * =====================================================================================
3 *
4 * Filename: dp.cpp
5 *
6 * Description:
7 *
8 * Version: 1.0
9 * Created: 2011年04月03日 11时50分53秒
10 * Revision: none
11 * Compiler: gcc
12 *
13 * Author: ronaflx
14 * Company: hit-acm-group
15 *
16 * =====================================================================================
17 */
18 #include <iostream>
19 #include <cstring>
20 #include <cstdio>
21  using namespace std;
22  const int N = 210;
23  const int T = 210;
24  int dp[T][N][N];
25  int a[T], b[T], d[T];
26  char maps[N][N];
27 int q[N], head, tail;
28 int abs(int x)
29 {
30 return x < 0 ? -x : x;
31 }
32 int cost(int t, int i, int j, int x, int y)
33 {
34 return dp[t][i][j] + abs(i - x) + abs(j - y);
35 }
36 int main()
37 {
38 int n, m, x, y, t;
39 while(scanf("%d %d %d %d %d", &n, &m, &x, &y, &t) == 5)
40 {
41 for(int i = 1;i <= n;i++)
42 scanf("%s", maps[i] + 1);
43 memset(dp, -1, sizeof(dp));
44 dp[0][x][y] = 0;
45 for(int i = 1;i <= t;i++)
46 {
47 scanf("%d %d %d", &a[i], &b[i], &d[i]);
48 if(d[i] == 1)
49 {
50 for(int j = 1;j <= m;j++)
51 {
52 head = tail = 0;
53 for(int k = n;k > 0;k--)
54 {
55 if(maps[k][j] == '.')
56 {
57 while(head < tail && q[head] - k > b[i] - a[i] + 1)
58 head++;
59 while(head < tail && cost(i - 1, q[tail - 1], j, k, j) < dp[i - 1][k][j])
60 tail--;
61 if(dp[i - 1][k][j] != -1)
62 q[tail++] = k;
63 if(head < tail)
64 dp[i][k][j] = max(dp[i][k][j], cost(i - 1, q[head], j, k, j));
65 }
66 if(dp[i][k][j] == -1)
67 head = tail = 0;
68 }
69 }
70 }
71 else if(d[i] == 2)
72 {
73 for(int j = 1;j <= m;j++)
74 {
75 head = tail = 0;
76 for(int k = 1;k <= n;k++)
77 {
78 if(maps[k][j] == '.')
79 {
80 while(head < tail && k - q[head] > b[i] - a[i] + 1)
81 head++;
82 while(head < tail && cost(i - 1, q[tail - 1], j, k, j) < dp[i - 1][k][j])
83 tail--;
84 if(dp[i - 1][k][j] != -1)
85 q[tail++] = k;
86 if(head < tail)
87 dp[i][k][j] = max(dp[i][k][j], cost(i - 1, q[head], j, k, j));
88 }
89 if(dp[i][k][j] == -1)
90 head = tail = 0;
91 }
92 }
93 }
94 else if(d[i] == 3)
95 {
96 for(int k = 1;k <= n;k++)
97 {
98 head = tail = 0;
99 for(int j = m;j > 0;j--)
100 {
101 if(maps[k][j] == '.')
102 {
103 while(head < tail && q[head] - j > b[i] - a[i] + 1)
104 head++;
105 while(head < tail && cost(i - 1, k, q[tail - 1], k, j) < dp[i - 1][k][j])
106 tail--;
107 if(dp[i - 1][k][j] != -1)
108 q[tail++] = j;
109 if(head < tail)
110 dp[i][k][j] = max(dp[i][k][j], cost(i - 1, k, q[head], k, j));
111 }
112 if(dp[i][k][j] == -1)
113 head = tail = 0;
114 }
115 }
116 }
117 else if(d[i] == 4)
118 {
119 for(int k = 1;k <= n;k++)
120 {
121 head = tail = 0;
122 for(int j = 1;j <= m;j++)
123 {
124 if(maps[k][j] == '.')
125 {
126 while(head < tail && j - q[head] > b[i] - a[i] + 1)
127 head++;
128 while(head < tail && cost(i - 1, k, q[tail - 1], k, j) < dp[i - 1][k][j])
129 tail--;
130 if(dp[i - 1][k][j] != -1)
131 q[tail++] = j;
132 if(head < tail)
133 dp[i][k][j] = max(dp[i][k][j], cost(i - 1, k, q[head], k, j));
134 }
135 if(dp[i][k][j] == -1)
136 head = tail = 0;
137 }
138 }
139 }
140 }
141 /*for(int i = 0;i <= t;i++)
142 {
143 printf("time %d\n", i);
144 for(int j = 1;j <= n;j++)
145 {
146 for(int k = 1;k <= m;k++)
147 printf("%2d ", dp[i][j][k]);
148 puts("");
149 }
150 }*/
151 int ans = 0;
152 for(int i = 1;i <= n;i++)
153 for(int j = 1;j <= m;j++)
154 ans = max(ans, dp[t][i][j]);
155 printf("%d\n", ans);
156 }
157 return 0;
158 }


http://www.zybbs.org/JudgeOnline/problem.php?id=1911

衡阳八中1911

1 /*
2 * =====================================================================================
3 *
4 * Filename: dp.cpp
5 *
6 * Description:
7 *
8 * Version: 1.0
9 * Created: 2011年04月17日 19时08分19秒
10 * Revision: none
11 * Compiler: gcc
12 *
13 * Author: ronaflx
14 * Company: hit-acm-group
15 *
16 * =====================================================================================
17 */
18 #include <iostream>
19 #include <vector>
20 #include <algorithm>
21 #include <cstdio>
22 using namespace std;
23 const int N = 1000001;
24 long long dp[N];
25 long long x[N], s[N];
26 int q[N];
27 long long a, b, c;
28 long long f(long long x)
29 {
30 return a * (x * x) + b * x + c;
31 }
32 long long Y(int i)
33 {
34 return dp[i] + a * (s[i] * s[i]) - b * s[i];
35 }
36 int main()
37 {
38 int n;
39 scanf("%d", &n);
40 scanf("%lld%lld%lld", &a, &b, &c);
41 dp[0] = s[0] = 0;
42 for(int i = 1; i <= n; i++)
43 {
44 scanf("%lld", &x[i]);
45 s[i] = s[i - 1] + x[i];
46 }
47 int head = 0, tail = 0;
48 q[tail++] = 0;
49 for(int i = 1; i <= n; i++)
50 {
51 while(head + 1 < tail)
52 {
53 long long y1 = Y(q[head]), y2 = Y(q[head + 1]);
54 long long x1 = s[q[head]], x2 = s[q[head + 1]];
55 if(y2 - y1 >= 2 * a * s[i] * (x2 - x1)) head++;
56 else break;
57 }
58 int k = q[head];
59 dp[i] = dp[k] + f(s[i] - s[k]);
60 while(head + 1 < tail)
61 {
62 long long y1 = Y(q[tail - 2]), y2 = Y(q[tail - 1]), y3 = Y(i);
63 long long x1 = s[q[tail - 2]], x2 = s[q[tail - 1]], x3 = s[i];
64 if((y3 - y2) * (x2 - x1) >= (y2 - y1) * (x3 - x2))
65 tail--;
66 else
67 break;
68 }
69 q[tail++] = i;
70 }
71 printf("%lld\n", dp[n]);
72 return 0;
73 }


裸斜率……把一个2写成1个,调试的这个纠结呀……
http://www.zybbs.org/JudgeOnline/problem.php?id=1010
衡阳八中1010
1 /*
2 * =====================================================================================
3 *
4 * Filename: dp.cpp
5 *
6 * Description:
7 *
8 * Version: 1.0
9 * Created: 2011年04月17日 19时08分19秒
10 * Revision: none
11 * Compiler: gcc
12 *
13 * Author: ronaflx
14 * Company: hit-acm-group
15 *
16 * =====================================================================================
17 */
18 #include <iostream>
19 #include <vector>
20 #include <algorithm>
21 #include <cstdio>
22 #define DEBUG(x) cout << #x << " " << x << endl;
23 using namespace std;
24 const int N = 50001;
25 long long dp[N];
26 long long x[N], s[N], f[N];
27 int q[N];
28 long long Y(int i)
29 {
30 return dp[i] + f[i] * f[i];
31 }
32 int main()
33 {
34 int n, l;
35 while(scanf("%d %d", &n, &l) == 2)
36 {
37 l++;
38 for(int i = 1;i <= n;i++)
39 {
40 scanf("%lld", &x[i]);
41 s[i] = s[i - 1] + x[i];
42 f[i] = s[i] + i;
43 }
44 dp[0] = 0;
45 int head = 0, tail = 0;
46 q[tail++] = 0;
47 for(int i = 1;i <= n;i++)
48 {
49 while(head + 1 < tail)
50 {
51 long long y1 = Y(q[head]), y2 = Y(q[head + 1]);
52 long long x1 = f[q[head]], x2 = f[q[head + 1]];
53 if(y2 - y1 < 2 * (f[i] - l) * (x2 - x1)) head++;
54 else break;
55 }
56 int j = q[head];
57 dp[i] = dp[j] + (f[i] - f[j] - l) * (f[i] - f[j] - l);
58 while(head + 1 < tail)
59 {
60 long long y1 = Y(q[tail - 2]), y2 = Y(q[tail - 1]), y3 = Y(i);
61 long long x1 = f[q[tail - 2]], x2 = f[q[tail - 1]], x3 = f[i];
62 if((y2 - y1) * (x3 - x2) >= (y3 - y2) * (x2 - x1)) tail--;
63 else break;
64 }
65 q[tail++] = i;
66 }
67 printf("%lld\n", dp[n]);
68 }
69 return 0;
70 }

维护极小值的斜率
posted @ 2011-02-05 14:19  ronaflx  阅读(...)  评论(...编辑  收藏