DP斜率优化总结

DP斜率优化总结

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

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

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

说一些注意事项:

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

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

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

复制代码
     
     
/*
* =====================================================================================
*
* 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 N = 100001 ;
double sum[N];
int a[N];
int n, len;
int q[N], head, tail;
double max( double a, double b)
{
return a < b ? b : a;
}
int main()
{
while (scanf( " %d %d " , & n, & len) == 2 )
{
sum[
0 ] = 0 ;
for ( int i = 1 ;i <= n;i ++ )
{
scanf(
" %d " , & a[i]);
sum[i]
= sum[i - 1 ] + a[i];
}
head
= tail = 0 ;
double ans = 0 ;
for ( int i = len;i <= n;i ++ )
{
int now = i - len;
while (head + 1 < tail)
{
double dy1 = sum[q[tail - 1 ]] - sum[q[tail - 2 ]], dy2 = sum[now] - sum[q[tail - 1 ]];
double dx1 = q[tail - 1 ] - q[tail - 2 ],dx2 = now - q[tail - 1 ];
if (dy1 * dx2 > dy2 * dx1)
tail
-- ;
else
break ;
}
q[tail
++ ] = now ++ ;
while (head + 1 < tail)
{
double y1 = sum[q[head]], y2 = sum[q[head + 1 ]],y3 = sum[i];
double x1 = q[head], x2 = q[head + 1 ], x3 = i;
if ((y3 - y1) * (x3 - x2) < (y3 - y2) * (x3 - x1))
head
++ ;
else
break ;
}
ans
= max(ans,(sum[i] - sum[q[head]]) / (i - q[head]));
}
printf(
" %.2lf\n " , ans);
}
return 0 ;
复制代码

另外的两道

hdu 3480 Division

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

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

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

x=a[j + 1];

维护斜率下凸性即可

复制代码
     
     
/*
* =====================================================================================
*
* 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,加深了对斜率优化的理解。

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

复制代码
     
     
/*
* =====================================================================================
*
* 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了无数次。
要仔细注意初始化的上下界,非常重要!!
复制代码
      
      
#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年汤泽 《




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




复制代码

      
      
/*
* =====================================================================================
*
* 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




复制代码

      
      
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


裸斜率……把一个2写成1个,调试的这个纠结呀……
http://www.zybbs.org/JudgeOnline/problem.php?id=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 }
复制代码

维护极小值的斜率
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值