Description
For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up in the same order. One day Farmer John decides to organize a game of Ultimate Frisbee with some of the cows. To keep things simple, he will take a contiguous range of cows from the milking lineup to play the game. However, for all the cows to have fun they should not differ too much in height.
Farmer John has made a list of Q (1 ≤ Q ≤ 200,000) potential groups of cows and their heights (1 ≤height ≤ 1,000,000). For each group, he wants your help to determine the difference in height between the shortest and the tallest cow in the group.
Input
Output
Sample Input
6 3 1 7 3 4 2 5 1 5 4 6 2 2
Sample Output
6 3 0
RMQ模板题,第一次接触,没太深理解。
http://dongxicheng.org/structure/lca-rmq/对LCA与RMQ的相关知识说的非常好,仔细研读。
主要在于RMQ的在线算法中对于区间的划分。
f[i,j]表示从第i个数起,连续2^j个数中的最大数。将i,j平分为两段f[i,j-1],f[i+2^(j-1),j]后,可得dp方程f[i,j]=max(f[i,j-1],f[i+2^(j-1),j])
当你要找寻从第i个数到第j个数时,由于可能无法直接找到该区间,可以改为找到两个区间,区间并集为该区间。
取k=log2(j-i+1),可得i,j之间最大值为max{F[i , k], F[ j - 2 ^ k + 1, k]}
即强行将区间用两个子区间合并代替。
贴个代码,没啥说的:
#include <algorithm> #include <iostream> #include <sstream> #include <cstring> #include <cstdlib> #include <string> #include <vector> #include <cstdio> #include <cmath> #include <queue> #include <stack> #include <map> #include <set> using namespace std; #define Max 200005 int cow[50005],maxs[50005][20],mins[50005][20]; void RMQ(int num){ for (int i=1; i<=num; i++) { maxs[i][0]=cow[i]; mins[i][0]=cow[i]; } for (int j=1; j<20; ++j) { for (int i=1; i<=num; ++i) { if (i+(1<<j)-1<=num) { maxs[i][j]=max(maxs[i][j-1],maxs[i+(1<<(j-1))][j-1]); mins[i][j]=min(mins[i][j-1],mins[i+(1<<(j-1))][j-1]); } } } } int main(){ int n,q,a,b,k,maxx,minn; while (cin>>n>>q) { for (int i=1; i<=n; i++) { scanf("%d",&cow[i]); } RMQ(n); for (int i=0; i<q; i++) { scanf("%d%d",&a,&b); k=log2(b-a+1); int temp=pow(2, k); maxx=max(maxs[a][k], maxs[b-temp+1][k]); minn=min(mins[a][k], mins[b-temp+1][k]); printf("%d\n",maxx-minn); } } return 0; }
下面添加一种线段树做法:
感觉做法上还是与RMQ有很大不同的,不过细细想下去又有些殊途同归。
无非就是线段树存区间内的最大值最小值,存完以后查询的时候按照构造子树的规则根据查找区间的大小进行调整,感觉用到了二分的思想。
大概就是这样吧了解太浅说不出来什么……最近都是看题解做题感觉对算法理解越来越浅……要改善。
#include <algorithm> #include <iostream> #include <sstream> #include <cstring> #include <cstdlib> #include <string> #include <vector> #include <cstdio> #include <cmath> #include <queue> #include <stack> #include <map> #include <set> using namespace std; #define INF 0x3f3f3f3 struct Node{ int l,r; int nmin,nmax; }segtree[600005]; int a[200005],nmax,nmin; void build(int i,int l,int r){ segtree[i].l=l; segtree[i].r=r; if (l==r) { segtree[i].nmin=segtree[i].nmax=a[l]; return; } int mid=(l+r)/2; build(i*2, l, mid); build(i*2+1, mid+1,r); segtree[i].nmin=min(segtree[i*2].nmin,segtree[i*2+1].nmin); segtree[i].nmax=max(segtree[i*2].nmax,segtree[i*2+1].nmax); } void query(int i,int l,int r){ if (segtree[i].nmax<=nmax&&segtree[i].nmin>=nmin) { return; } if (segtree[i].l==l&&segtree[i].r==r) { nmax=max(segtree[i].nmax,nmax); nmin=min(segtree[i].nmin,nmin); return ; } int mid=(segtree[i].l+segtree[i].r)/2; if (r<=mid) query(i*2, l, r); else if (l>mid) query(i*2+1, l, r); else { query(i*2, l, mid); query(i*2+1, mid+1, r); } } int main(){ int n,l,r,q; while (scanf("%d %d",&n,&q)!=EOF) { for (int i=1; i<=n; i++) { scanf("%d",&a[i]); } build(1, 1, n); for (int i=1; i<=q; i++) { scanf("%d%d",&l,&r); nmax=-INF,nmin=INF; query(1, l, r); printf("%d\n",nmax-nmin); } } return 0; }