士兵杀敌(三)
时间限制:
2000 ms | 内存限制:
65535 KB
难度:
5
-
描述
-
南将军统率着N个士兵,士兵分别编号为1~N,南将军经常爱拿某一段编号内杀敌数最高的人与杀敌数最低的人进行比较,计算出两个人的杀敌数差值,用这种方法一方面能鼓舞杀敌数高的人,另一方面也算是批评杀敌数低的人,起到了很好的效果。
所以,南将军经常问军师小工第i号士兵到第j号士兵中,杀敌数最高的人与杀敌数最低的人之间军功差值是多少。
现在,请你写一个程序,帮小工回答南将军每次的询问吧。
注意,南将军可能询问很多次。
-
输入
-
只有一组测试数据
第一行是两个整数N,Q,其中N表示士兵的总数。Q表示南将军询问的次数。(1<N<=100000,1<Q<=1000000)
随后的一行有N个整数Vi(0<=Vi<100000000),分别表示每个人的杀敌数。
再之后的Q行,每行有两个正正数m,n,表示南将军询问的是第m号士兵到第n号士兵。
输出
- 对于每次询问,输出第m号士兵到第n号士兵之间所有士兵杀敌数的最大值与最小值的差。 样例输入
-
5 2 1 2 6 9 3 1 2 2 4
样例输出
-
1 7
解题思路:
RMQ问题:RMQ问题用到是动态规划的思想,比如某题要求的是(i,j)区间内的最大值或最小值,RMQ采用的ST算法,ST算法是一种支持在线的算法,ST算法的大致思路为:预处理:
假设给定一个序列1,3,4,2,7,8,4,8,定义F(i,j)表示为从i开始2^j个数的最大值,也就是说这个区间从i开始到i+(1<<j)-1结束,在序列中假设从1开始,F(1,3)等价于区间[1,8],也就是序列1,3,4,2,7,8,4,8整个序列,同理F(1,2)表示区间[1,4]也就是序列1,3,4,2,以此类推我们可以知道F(i,0)代表第i个元素,也就是数组下标从1开始的第i个数值。接下来我们把整个序列分成相等长度的两段,分为[i,i+(1<<(j-1)-1)]和[i+(1<<(j-1)),i+(1<<j) -1](长度都等于2^(j-1)),当i=1,j=3时,表示两个区间(1,3,4,2)和(7,8,4,8),由此我们可以得到动态规划的状态转移方程为 P[i][j] = max or min (P[i][j-1],P[i+(1<<(j-1)][j-1]).
查询:
假设要查询的区间为(i,j),我们取k = log2(j-i+1), 则有:RMQ(A, i, j)=max{F[i , k], F[ j - 2 ^ k + 1, k]},举例说明,要求区间[2,8]的最大值,k = log2(8 - 2 + 1)= 2,即求max(F[2, 2],F[8 - 2 ^ 2 + 1, 2]) = max(F[2, 2],F[5, 2]);
AC代码:
#include<iostream> #include<algorithm> #include<cmath> #include<cstdio> using namespace std; const int maxn = 100010; int maxsum[maxn][20]; int minsum[maxn][20]; /* 把F[i,j]平均分成两段(因为f[i,j]一定是偶数个数字),从i到i+2^(j-1)-1为一段, i+2^(j-1)到 i+2^j-1为一段(长度都为2^(j-1)) */ void RMQ(int num) { for(int j=1;j<20;j++) { for(int i=1;i<=num;i++) { if(i + (1<<j) - 1 <= num) { maxsum[i][j] = max(maxsum[i][j-1],maxsum[i+(1<<(j-1))][j-1]); //dp的思想 minsum[i][j] = min(minsum[i][j-1],minsum[i+(1<<(j-1))][j-1]); } } } } int main() { int m,n; int i,j; int start,end; cin>>m>>n; for(i=1;i<=m;i++) { scanf("%d",&maxsum[i][0]); //cin>>maxsum[i][0]; //表示他本身 i + 2 ^ 0 - 1表示a[i]; minsum[i][0] = maxsum[i][0]; } RMQ(m); while(n--) { //cin>>start>>end; scanf("%d%d",&start,&end); int k = (int)(log(end - start + 1.0) / log(2.0)); int maxres = max(maxsum[start][k],maxsum[end - (1 << k) + 1][k]); int minres = min(minsum[start][k],minsum[end - (1 << k) + 1][k]); printf("%d\n",maxres - minres); } return 0; }
下面是RMQ返回下标的模版#include <iostream> #include <algorithm> #include <cstring> #include <string> #include <cstdio> #include <cmath> #include <queue> #include <map> #include <set> #define eps 1e-5 #define MAXN 55555 #define MAXM 11111 #define INF 1000000000 #define lch(x) x<<1 #define rch(x) x<<1|1 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 using namespace std; int mi[MAXN][17], mx[MAXN][17], w[MAXN]; int n, q; void rmqinit() { for(int i = 1; i <= n; i++) mi[i][0] = mx[i][0] = i; int m = (int)(log(n * 1.0) / log(2.0)); for(int i = 1; i <= m; i++) for(int j = 1; j <= n; j++) { mx[j][i] = mx[j][i - 1]; mi[j][i] = mi[j][i - 1]; if(j + (1 << (i - 1)) <= n) { if(w[mx[j][i]] < w[mx[j + (1 << (i - 1))][i - 1]]) mx[j][i] = mx[j + (1 << (i - 1))][i - 1]; if(w[mi[j][i]] > w[mi[j + (1 << (i - 1))][i - 1]]) mi[j][i] = mi[j + (1 << (i - 1))][i - 1]; } } } int rmqmin(int l,int r) { int m = (int)(log((r - l + 1) * 1.0) / log(2.0)); if(w[mi[l][m]] > w[mi[r - (1 << m) + 1][m]]) return mi[r - (1 << m) + 1][m]; else return mi[l][m]; } int rmqmax(int l,int r) { int m = (int)(log((r - l + 1) * 1.0) / log(2.0)); if(w[mx[l][m]] < w[mx[r - (1 << m) + 1][m]]) return mx[r - (1 << m) + 1][m]; else return mx[l][m]; } int main() { scanf("%d%d", &n, &q); for(int i = 1; i <= n; i++) scanf("%d", &w[i]); rmqinit(); int l, r; while(q--) { scanf("%d%d", &l, &r); printf("%d\n", rmqmax(l, r)); } return 0; }
-
-
RMQ返回元素模版:
-
#include <iostream> #include <algorithm> #include <cstring> #include <string> #include <cstdio> #include <cmath> #include <queue> #include <map> #include <set> #define eps 1e-5 #define MAXN 55555 #define MAXM 11111 #define INF 1000000000 #define lch(x) x<<1 #define rch(x) x<<1|1 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 using namespace std; int mi[MAXN][17], mx[MAXN][17], w[MAXN]; int n, q; void rmqinit() { for(int i = 1; i <= n; i++) mi[i][0] = mx[i][0] = w[i]; int m = (int)(log(n * 1.0) / log(2.0)); for(int i = 1; i <= m; i++) for(int j = 1; j <= n; j++) { mx[j][i] = mx[j][i - 1]; if(j + (1 << (i - 1)) <= n) mx[j][i] = max(mx[j][i], mx[j + (1 << (i - 1))][i - 1]); mi[j][i] = mi[j][i - 1]; if(j + (1 << (i - 1)) <= n) mi[j][i] = min(mi[j][i], mi[j + (1 << (i - 1))][i - 1]); } } int rmqmin(int l,int r) { int m = (int)(log((r - l + 1) * 1.0) / log(2.0)); return min(mi[l][m] , mi[r - (1 << m) + 1][m]); } int rmqmax(int l,int r) { int m = (int)(log((r - l + 1) * 1.0) / log(2.0)); return max(mx[l][m] , mx[r - (1 << m) + 1][m]); } int main() { scanf("%d%d", &n, &q); for(int i = 1; i <= n; i++) scanf("%d", &w[i]); rmqinit(); int l, r; while(q--) { scanf("%d%d", &l, &r); printf("%d\n", rmqmax(l, r)); } return 0; }
-
只有一组测试数据