单调队列:http://baike.baidu.com/view/3771451.htm
foj 1894 志愿者选拔
http://acm.fzu.edu.cn/problem.php?pid=1894
题意:中文略..
思路:
就是很单纯的单调队列,最最入门的单调队列,而且是很形象的排队问题。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define ll long long #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 1000007 #define N 5007 #define M 200007 using namespace std; struct node { char name[6]; int rp,num;//记录人品,记录编号 }q[maxn]; int main() { //freopen("din.txt","r",stdin); int t; char op[10]; scanf("%d",&t); while (t--) { scanf("%s",op); int front = 0,tail = -1; int num = 0,leave = 0; while (scanf("%s",op)) { if (op[0] == 'E') break; if (op[0] == 'C') { node tmp; scanf("%s%d",tmp.name,&tmp.rp); tmp.num = ++num; while (tail >= front && q[tail].rp < tmp.rp) tail--;//维护单调性 q[++tail] = tmp; } else if (op[0] == 'Q') { while (tail >= front && q[front].num <= leave) front++;//找到还没出对的最大值 if (tail >= front) printf("%d\n",q[front].rp); else puts("-1"); } else leave++; } } return 0; }
pku 2823 Sliding Window
http://poj.org/problem?id=2823
题意:
给定长度为n的序列,求从前往后没k个数里面的最大值最小值。
思路:
单调队列维护区间长度为k的队列并取最值,一个维护最大一个维护最小。注意这里队列里面存储的是坐标队头与当前进队的坐标之间的区间最值,开始一直转不过弯了,我们利用这一点来保持队列的维护长度为k.
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define ll long long #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 1000007 #define N 5007 #define M 200007 using namespace std; int a[maxn],ans[maxn]; int q[maxn]; void solve(int mk,int n,int k)//mk为0维护最小,1维护最大 { int front = 0,tail = - 1; int i; for (i = 0; i < k; ++i) { if (!mk) { while (tail >= front && a[i] < a[q[tail]]) tail--; q[++tail] = i; } else { while (tail >= front && a[i] > a[q[tail]]) tail--; q[++tail] = i; } } int len = 0; ans[len++] = a[q[front]]; for (i = k; i < n; ++i) { while (tail >= front && i - q[front] >= k) front++;//i是当前进队列的坐标与q[front]的差值即为我们队列维护的长度 if (!mk) { while (tail >= front && a[i] < a[q[tail]]) tail--;//进队 q[++tail] = i; } else { while (tail >= front && a[i] > a[q[tail]]) tail--;//进队 q[++tail] = i; } ans[len++] = a[q[front]]; } for (i = 0; i < len - 1; ++i) printf("%d ",ans[i]); printf("%d\n",ans[i]); } int main() { //freopen("din.txt","r",stdin); int n,k,i; scanf("%d%d",&n,&k); for (i = 0; i < n; ++i) scanf("%d",&a[i]); solve(0,n,k); solve(1,n,k); return 0; }
由于本题是维护区间最值问题所以可以用线段树和rmq做,线段树很简单毫无压力的区间求值,而rmq这里还会卡内存,我们处理的方法是由于他求的区间长度总k是一个定值,多以我们的f只要开的一维即可。
线段树代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define ll long long #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define ls (rt<<1) #define rs (rt<<1|1) #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 1000007 #define N 5007 #define M 200007 using namespace std; int a[maxn<<2],b[maxn<<2],ans1[maxn],ans2[maxn]; void pushup(int rt) { a[rt] = max(a[ls],a[rs]); b[rt] = min(b[ls],b[rs]); } void build(int l,int r,int rt) { if (l == r) { scanf("%d",&a[rt]); b[rt] = a[rt]; return ; } int m = (l + r)>>1; build(lc); build(rc); pushup(rt); } int get(int L,int R,int l,int r,int rt,int mk) { if (l >= L && r <= R) { if (!mk) return b[rt]; else return a[rt]; } int m = (l + r)>>1; int res = mk == 0 ? inf:-inf; if (L <= m) { if (!mk) res = min(res,get(L,R,lc,mk)); else res = max(res,get(L,R,lc,mk)); } if (R > m) { if (!mk) res = min(res,get(L,R,rc,mk)); else res = max(res,get(L,R,rc,mk)); } return res; } int main() { //freopen("din.txt","r",stdin); int n,k,i; scanf("%d%d",&n,&k); build(1,n,1); int s = 1,e = k; int len = 0; for (i = k; i <= n; ++i) { ans1[len] = get(s,e,1,n,1,0); ans2[len++] = get(s,e,1,n,1,1); s++,e++; } for (i = 0; i < len - 1; ++i) printf("%d ",ans1[i]); printf("%d\n",ans1[i]); for (i = 0; i < len - 1; ++i) printf("%d ",ans2[i]); printf("%d\n",ans2[i]); return 0; }
rmq代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define ll long long #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 1000007 #define N 5007 #define M 200007 using namespace std; int f[maxn],val[maxn]; int n,k; void initMIN() { int i,j,m; for (i = 1; i <= n; ++i) f[i] = val[i]; m = log(1.0*k)/log(2.0); for (i = 1; i <= m; ++i) { for (j = 1; j + (1<<i) - 1 <= n; ++j) f[j] = min(f[j],f[j + (1<<(i - 1))]); } } void initMAX() { int i,j,m; for (i = 1; i <= n; ++i) f[i] = val[i]; m = log(1.0*k)/log(2.0);//我们只要求得m即可f[j]存储的是从j开始长度为2^m次方的最值 for (i = 1; i <= m; ++i) { for (j = 1; j + (1<<i) - 1 <= n; ++j) f[j] = max(f[j],f[j + (1<<(i - 1))]); } } int rmq(int s,int e,int mk) { int m = log(1.0*k)/log(2.0); if (mk == 1) return max(f[s],f[e - (1<<m) + 1]); else return min(f[s],f[e - (1<<m) + 1]); } int main() { //freopen("din.txt","r",stdin); int i; scanf("%d%d",&n,&k); for (i = 1; i <= n; ++i) scanf("%d",&val[i]); initMIN(); int s,e; s = 1; e = k; for (i = k; i <= n; ++i) { if (i != n) printf("%d ",rmq(s,e,0)); else printf("%d\n",rmq(s,e,0)); s++; e++; } initMAX(); s = 1; e = k; for (i = k; i <= n; ++i) { if (i != n) printf("%d ",rmq(s,e,1)); else printf("%d\n",rmq(s,e,1)); s++; e++; } return 0; }
hdu3415 Max Sum of Max-K-sub-sequence
http://acm.hdu.edu.cn/showproblem.php?pid=3415
题意:
给你一个环游N个元素组成,求最大的最多有k个连续元素的子串的最大和,起点肯定为1-n,我们在处理的复制n以后的k个来处理;
思路:
单调队列维护k长度区间内1-i的和的最小值,而我们要枚举的区间的和,就是在满足k的前提下,a[i] - a[q[front] - 1] a[i]表示1-i的区间和。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define ll long long #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define N 5007 #define M 200007 using namespace std; int q[2*maxn]; int a[2*maxn]; int n,k; int main() { //freopen("din.txt","r",stdin); int i,t; scanf("%d",&t); while (t--) { scanf("%d%d",&n,&k); a[0] = 0; for (i = 1; i <= n; ++i) { scanf("%d",&a[i]); a[i] += a[i - 1]; } for (i = n + 1; i <= n + k; ++i)//把从n以后的k个复制 a[i] = a[n] + a[i - n]; int front = 0,tail = -1; int MAX = -inf; int s = 0,e = 0; for (i = 1; i <= n + k; ++i) { while (tail >= front && i - q[front] >= k) front++;//单调队列维持其维护的长度 while (tail >= front && a[q[tail] - 1] > a[i - 1]) tail--;//其存储1-i的和最小的 q[++tail] = i; if (a[i] - a[q[front] - 1] > MAX)//每次计算一下最值 { MAX = a[i] - a[q[front] - 1]; s = q[front]; e = i; if (e > n) e -= n; } } printf("%d %d %d\n",MAX,s,e); } return 0; }
hdu: Subsequence
http://acm.hdu.edu.cn/showproblem.php?pid=3530
http://hi.baidu.com/fhnstephen/blog/item/9e908215d2694415962b4331.html这里的解题思路比较详细,注意这里他说如果队尾元素和当前元素相等,可以照样删除,因为同样的元素只要有一个就行了,而且是最后的一个。而我做的时候是留下的觉得如果遇到相同的最小值,我们要用最小值的最小坐标来计算最大的长度。比如如果最小值为2 有两个坐标分别为1 2 最大值坐标为7我们肯定用7 - 1来获得最大长度。ps:这里的子序列必须是连续的。。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define ll long long #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 100007 #define N 100007 using namespace std; int a[N]; int q1[N],q2[N]; int main() { //freopen("din.txt","r",stdin); int n,m,k,i; while (~scanf("%d%d%d",&n,&m,&k)) { for (i = 1; i <= n; ++i) scanf("%d",&a[i]); int ans = 0, now = 0; int h1 = 0, h2 = 0; int t1 = 0,t2 = -1; for (i = 1; i <= n; ++i) { while (t1 >= h1 && a[q1[t1]] < a[i]) t1--; q1[++t1] = i; while (t2 >= h2 && a[q2[t2]] > a[i]) t2--; q2[++t2] = i; while (a[q1[h1]] - a[q2[h2]] > k) { if (q1[h1] < q2[h2]) now = q1[h1++]; else now = q2[h2++]; } if (a[q1[h1]] - a[q2[h2]] >= m && a[q1[h1]] - a[q2[h2]] <= k) ans = max(ans,i - now); } printf("%d\n",ans); } return 0; }