一、学习
1.sha崽
2.几篇论文
二、练习题
1.Sliding Window [pku-2823]
分析:单调队列入门级别的题目。单调队列比普通队列多了一个操作---队尾删除。队列元素有两个域,<index,value>。由于元素是按index(下标)从小到大插入的,所以index在队列中保持单调递增;为保持value的单调性,可以在队尾插入时实现。而队首元素就是所需要的最小值(或最大值),每个元素最多进队一次,出队一次,复杂度为O(n)。
#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]。
#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 .
#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,随着右端的右移(即插入元素),区间的最大、最小值也发生着变化,那么就要判断变化以后是否满足题目条件,如果不满足,则左端点进行右移。
#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 ;
}
分析: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汗!