DP[用单调性优化][专辑]

一、学习

1.sha崽

2.几篇论文

 

二、练习题

1.Sliding Window [pku-2823]

分析:单调队列入门级别的题目。单调队列比普通队列多了一个操作---队尾删除。队列元素有两个域,<index,value>。由于元素是按index(下标)从小到大插入的,所以index在队列中保持单调递增;为保持value的单调性,可以在队尾插入时实现。而队首元素就是所需要的最小值(或最大值),每个元素最多进队一次,出队一次,复杂度为O(n)。

 

pku-2823
   
     
#include < stdio.h >
#include
< string .h >
#include
< stdlib.h >
#include
< ctype.h >
#include
< math.h >
#include
< set >
#include
< map >
#include
< queue >
#include
< vector >
#include
< algorithm >

using namespace std;

// #define SUPERBIN
#define NL1 1000005
#define NL2 21
#define EP 1e-10
#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))
#define LL long long

int d[NL1], stk[NL1], stk1[NL1], dt[NL1];
int n, k;

int main() {
#ifdef SUPERBIN
freopen(
" in.txt " , " r " , stdin);
freopen(
" out.txt " , " w " , stdout);
#endif
int T, t, r, cs = 1 ;
int i, j, k, a, b, c, x, y, z;
int front, tail;
int front1, tail1;
while (scanf( " %d%d " , & n, & k) != EOF) {
front
= tail = 0 ;
front1
= tail1 = 0 ;
k
= k > n ? n : k;
for (i = 0 ; i < n; i ++ ) scanf( " %d " , & dt[i]);
for (i = 0 ; i < k - 1 ; i ++ ) {
while (tail > front && dt[stk[tail - 1 ]] >= dt[i]) tail -- ;
while (tail1 > front1 && dt[stk1[tail1 - 1 ]] <= dt[i]) tail1 -- ;
stk[tail
++ ] = i;
stk1[tail1
++ ] = i;
}
j
= 0 ;
x
= i;
while (i < n) {
while (tail > front && dt[stk[tail - 1 ]] >= dt[i]) tail -- ;
stk[tail
++ ] = i;
if (stk[front] < i - k + 1 ) front ++ ;
d[i]
= stk[front];
i
++ ;
}
for (i = k - 1 ; i < n; i ++ )
printf(
" %d%c " , dt[d[i]], i == n - 1 ? ' \n ' : ' ' );
j
= 0 ;
i
= x;
while (i < n) {
while (tail1 > front1 && dt[stk1[tail1 - 1 ]] <= dt[i]) tail1 -- ;
stk1[tail1
++ ] = i;
if (stk1[front1] < i - k + 1 ) front1 ++ ;
d[i]
= stk1[front1];
i
++ ;
}
for (i = k - 1 ; i < n; i ++ )
printf(
" %d%c " , dt[d[i]], i == n - 1 ? ' \n ' : ' ' );
}
return 0 ;
}
/*
* DP+单调队列优化
*/

 

 

2.Max Sum of Max-K-sub-sequence  [hdu-3415]

分析:令s[i]为前i个元素的和,序列长度增加K,DP方程为:d[i] = MAX(s[i]-s[j]),(i-j<=K) 其中s[i]-s[j]表示序列[j-1,i]的和。利用单调队列维护s[j]。

 

hdu-3415
   
     
#include < stdio.h >
#include
< string .h >
#include
< stdlib.h >
#include
< ctype.h >
#include
< math.h >
#include
< set >
#include
< map >
#include
< queue >
#include
< vector >
#include
< algorithm >

using namespace std;

// #define SUPERBIN
#define NL1 100004
#define NL2 NL1*2
#define EP 1e-10
#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))
#define LL long long

int d[NL2], st[NL2], w[NL2], sum[NL2];
int n, k;

int main() {
#ifdef SUPERBIN
freopen(
" in.txt " , " r " , stdin);
freopen(
" out.txt " , " w " , stdout);
#endif
int T, t, r, cs = 1 ;
int i, j, a, b, c, x, y, z, n1;
scanf(
" %d " , & T);
while (T -- ) {
scanf(
" %d%d " , & n, & k);
n1
= n;
for (i = 1 ; i <= n; i ++ ) scanf( " %d " , & w[i]);
n
+= k;
for (j = 1 ; i <= n; i ++ , j ++ ) w[i] = w[j];
for (i = 1 ,sum[ 0 ] = 0 ; i <= n; i ++ ) {
sum[i]
= sum[i - 1 ] + w[i];
}
int front, rear;
front
= rear = 0 ;
i
= 0 ;
int mx, mxl, mxr;
mx
= - 0xfffffff ;
while (i < n) {
while (front < rear && sum[st[rear - 1 ]] >= sum[i]) rear -- ;
st[rear
++ ] = i;
while (front < rear && i + 1 - st[front] > k) front ++ ;
x
= sum[i + 1 ] - sum[st[front]];
if (x > mx) {
mx
= x;
mxl
= st[front] + 1 ; // key
mxr = i + 1 ;
}
i
++ ;
}
printf(
" %d %d %d\n " , mx, mxl, mxr > n1 ? mxr - n1:mxr);
}

return 0 ;
}

 

 

3.Trade [hdu-3401]

分析:d[i][j]表示在第i天持有股票j的最大收益。根据三种决策(买,卖,nothing)往前推。

d[i][j] = MAX( d[i-W-1][k] - AP[i]*(j-k) ) (j-k<=AS[i]),[在第 i 天购买 j-k 支股 得到 j 支股]

d[i][j] = MAX( d[i-W-1][k] + BP[i]*(k-j) ) (k-j<=Bs[i]),[在第 i 天卖出 k-j 支股 得到 j 支股]

d[i][j] = MAX( d[i][j], d[i-1][j] ),[在第 i 天不买也不卖]

前两个方程的决策 k 可以用单调队列进行优化,只须稍加变形:

d[i][j] = MAX(d[i-W-1][k] + AP[i]*k) - AP[i]*j ;

d[i][j] = MAX(d[i-W-1][k] + BP[i]*k) - BP[i]*j .

 

hdu-3401
   
     
#include < stdio.h >
#include
< string .h >
#include
< stdlib.h >
#include
< ctype.h >
#include
< math.h >
#include
< set >
#include
< map >
#include
< queue >
#include
< vector >
#include
< algorithm >

using namespace std;

// #define SUPERBIN
#define NL1 2011
#define NL2 21
#define EP 1e-10
#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))
#define LL long long

int d[NL1][NL1], AP, BP, AS, BS;
int st1[NL1][ 2 ], front1, rear1;
int W, T, MaxP;

int main() {
#ifdef SUPERBIN
freopen(
" in.txt " , " r " , stdin);
freopen(
" out.txt " , " w " , stdout);
#endif
int t, i, j, k, a, b, c, x, y, z;
scanf(
" %d " , & t);
while (t -- ) {
scanf(
" %d%d%d " , & T, & MaxP, & W);
for (i = 1 ; i <= T; i ++ ) {
scanf(
" %d%d%d%d " , & AP, & BP, & AS, & BS);
for (j = 0 ; j <= MaxP; j ++ ) d[i][j] = - 0x7fffffff ;
if (i <= W + 1 ) {
for (j = 0 ; j <= MaxP && j <= AS; j ++ ) {
d[i][j]
= - AP * j;
}
}
if (i > 1 ) { // key
for (j = 0 ; j <= MaxP; j ++ )
d[i][j]
= MAX(d[i][j], d[i - 1 ][j]);
}
if (i <= W + 1 ) continue ;

front1
= rear1 = 0 ;
for (j = 0 ; j <= MaxP; j ++ ) {
k
= j >= AS ? (j - AS) : 0 ;
x
= d[i - W - 1 ][j] + AP * j;
while (front1 < rear1 && st1[rear1 - 1 ][ 1 ] <= x) rear1 -- ;
st1[rear1][
0 ] = j;
st1[rear1
++ ][ 1 ] = x;
while (front1 < rear1 && st1[front1][ 0 ] < k) front1 ++ ;

d[i][j]
= MAX(d[i][j], st1[front1][ 1 ] - AP * j);
}

front1
= rear1 = 0 ;
for (j = MaxP; j >= 0 ; j -- ) {
k
= j + BS <= MaxP ? (j + BS) : MaxP;
x
= d[i - W - 1 ][j] + BP * j;
while (front1 < rear1 && st1[rear1 - 1 ][ 1 ] <= x) rear1 -- ;
st1[rear1][
0 ] = j;
st1[rear1
++ ][ 1 ] = x;
while (front1 < rear1 && st1[front1][ 0 ] > k) front1 ++ ;
d[i][j]
= MAX(d[i][j], st1[front1][ 1 ] - BP * j);
}

}
int mx = 0 ;
for (j = 0 ; j <= MaxP; j ++ ) {
mx
= MAX(mx, d[T][j]);
}
printf(
" %d\n " , mx);
}
return 0 ;
}

 

 

4.Subsequence [hdu-3530]

分析:跟前面的一些题不一样,此题已知区间最大、最小值的差的范围,求区间最大长度。依然用单调队列维护区间的最大、最小值,区间的右端是i,那怎样确定

区间的左端呢?最初,左端是1,随着右端的右移(即插入元素),区间的最大、最小值也发生着变化,那么就要判断变化以后是否满足题目条件,如果不满足,则左端点进行右移。

 

hdu-3530
   
     
#include < stdio.h >
#include
< string .h >
#include
< stdlib.h >
#include
< ctype.h >
#include
< math.h >
#include
< set >
#include
< map >
#include
< string >
#include
< queue >
#include
< vector >
#include
< algorithm >

using namespace std;

// #define SUPERBIN
#define NL1 100011
#define NL2 21
#define EP 1e-10
#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))
#define LL long long

int w[NL1], st[NL1], st1[NL1];
int front1, rear1, front, rear;
int n, m, k;

int main() {
#ifdef SUPERBIN
freopen(
" in.txt " , " r " , stdin);
freopen(
" out.txt " , " w " , stdout);
#endif
int T, t, r, cs = 1 ;
int i, j, a, b, c, x, y, z;
while (scanf( " %d%d%d " , & n, & m, & k) != EOF) {
for (i = 1 ; i <= n; i ++ ) scanf( " %d " , & w[i]);
front
= rear = front1 = rear1 = 0 ;
int left = 1 ;
x
= 0 ;
for (i = 1 ; i <= n; i ++ ) {
while (front < rear && w[st[rear - 1 ]] <= w[i]) rear -- ;
st[rear
++ ] = i;
while (front1 < rear1 && w[st1[rear1 - 1 ]] >= w[i]) rear1 -- ;
st1[rear1
++ ] = i;
while (w[st[front]] - w[st1[front1]] > k) {
if (st[front] < st1[front1]) left = st[front ++ ] + 1 ; // left记录右移以后的左端点
else left = st1[front1 ++ ] + 1 ;
}
if (w[st[front]] - w[st1[front1]] >= m) {
x
= MAX(x,i - left + 1 );
}
}
printf(
" %d\n " , x);
}
return 0 ;
}

 

5.NOI-2005- 瑰丽的华尔兹

分析:d[k][i][j]表示在第k个时间段,划到位置(i,j)的最优值。在划动方向,和划动范围确定的情况下,d[k][i][j] = MAX(d[k-1][i1][j1] + dist),(i1,j1)为划动的起始位置,dist为划动的距离。因为划动是在一条直线上,可以用单调队列优化。

比如:当dir = 1 (方向为北),d[k][i][j] = MAX(d[k-1][i1][j]+i-i1) = MAX(d[k-1][i1][j]-i1) + i ,(i-i1<end-start+1).

 

瑰丽华尔兹
   
     
#include < stdio.h >
#include
< string .h >
#include
< stdlib.h >
#include
< ctype.h >
#include
< math.h >
#include
< set >
#include
< map >
#include
< string >
#include
< queue >
#include
< vector >
#include
< algorithm >

using namespace std;

// #define SUPERBIN
#define NL1 211
#define NL2 21
#define EP 1e-10
#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))
#define LL long long

char mp[NL1][NL1];
int d[NL1][NL1][NL1];
int st[NL1][ 2 ];
int n, m, x, y, k;

int main() {
#ifdef SUPERBIN
freopen(
" in.txt " , " r " , stdin);
freopen(
" out.txt " , " w " , stdout);
#endif
int T, r, cs = 1 ;
int i, j, j1, j2, a, b, c, s, t, dir;
while (scanf( " %d%d%d%d%d " , & n, & m, & x, & y, & k) != EOF) {
for (i = 1 ; i <= n; i ++ ) scanf( " %s " , mp[i] + 1 );
memset(d,
- 1 , sizeof (d));
d[
0 ][x][y] = 0 ;
for (i = 1 ; i <= k; i ++ ) {
scanf(
" %d%d%d " , & s, & t, & dir);
int front, rear;
switch (dir) {
case 1 :
for (j1 = 1 ; j1 <= m; j1 ++ ) {
front
= rear = 0 ;
for (j2 = n; j2 >= 1 ; j2 -- ) {
if (mp[j2][j1] == ' . ' ) {
if (d[i - 1 ][j2][j1] >= 0 ) {
while (front < rear && st[rear - 1 ][ 1 ] <= d[i - 1 ][j2][j1] + j2) rear -- ; // key: 别忘了加距离
st[rear][ 0 ] = j2;
st[rear
++ ][ 1 ] = d[i - 1 ][j2][j1] + j2;
}
while (front < rear && st[front][ 0 ] - j2 > t - s + 1 ) front ++ ;
if (front < rear) d[i][j2][j1] = MAX(d[i][j2][j1], st[front][ 1 ] - j2);
}
else {
front
= rear = 0 ;
}
}
}
break ;
case 2 :
for (j1 = 1 ; j1 <= m; j1 ++ ) {
front
= rear = 0 ;
for (j2 = 1 ; j2 <= n; j2 ++ ) {
if (mp[j2][j1] == ' . ' ) {
if (d[i - 1 ][j2][j1] >= 0 ) {
while (front < rear && st[rear - 1 ][ 1 ] <= d[i - 1 ][j2][j1] - j2) {
rear
-- ;
}
st[rear][
0 ] = j2;
st[rear
++ ][ 1 ] = d[i - 1 ][j2][j1] - j2;
}
while (front < rear && j2 - st[front][ 0 ] > t - s + 1 ) front ++ ;
if (front < rear) d[i][j2][j1] = MAX(d[i][j2][j1], st[front][ 1 ] + j2);
}
else {
front
= rear = 0 ;
}
}
}
break ;
case 3 :
for (j1 = 1 ; j1 <= n; j1 ++ ) {
front
= rear = 0 ;
for (j2 = m; j2 >= 1 ; j2 -- ) {
if (mp[j1][j2] == ' . ' ) {
if (d[i - 1 ][j1][j2] >= 0 ) {
while (front < rear && st[rear - 1 ][ 1 ] <= d[i - 1 ][j1][j2] + j2) rear -- ;
st[rear][
0 ] = j2;
st[rear
++ ][ 1 ] = d[i - 1 ][j1][j2] + j2;
}
while (front < rear && st[front][ 0 ] - j2 > t - s + 1 ) front ++ ;
if (front < rear) d[i][j1][j2] = MAX(d[i][j1][j2], st[front][ 1 ] - j2);
}
else {
front
= rear = 0 ;
}
}
}
break ;
case 4 :
for (j1 = 1 ; j1 <= n; j1 ++ ) {
front
= rear = 0 ;
for (j2 = 1 ; j2 <= m; j2 ++ ) {
if (mp[j1][j2] == ' . ' ) {
if (d[i - 1 ][j1][j2] >= 0 ) {
while (front < rear && st[rear - 1 ][ 1 ] <= d[i - 1 ][j1][j2] - j2) rear -- ;
st[rear][
0 ] = j2;
st[rear
++ ][ 1 ] = d[i - 1 ][j1][j2] - j2;
}
while (front < rear && j2 - st[front][ 0 ] > t - s + 1 ) front ++ ;
if (front < rear) d[i][j1][j2] = MAX(d[i][j1][j2], st[front][ 1 ] + j2);
}
else {
front
= rear = 0 ;
}
}
}
break ;
}
}
int ans = 0 ;
for (i = 1 ; i <= n; i ++ ) {
for (j = 1 ; j <= m; j ++ ) {
ans
= MAX(ans, d[k][i][j]);
}
}
printf(
" %d\n " , ans);
}
return 0 ;
}

 

 

PS:相当不好想,实现也费了好大功夫,1更搞笑的是我居然把 stk[NL1][2]数组开成 stk[NL][0],直接导致了n莫名其妙的改变了,写了老长的输出才查出来,狂⊙﹏⊙b汗!

你可能感兴趣的:(优化)