经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
栈与队列(含单调栈与单调队列)
来源:cnblogs  作者:qzhwlzy  时间:2021/6/21 10:12:52  对本文有异议

算法思路

栈(\(stack\))又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。 ----摘自 百度百科

附图

由上述资料可知,栈是一个“先进后出”的数据结构。是只能在栈顶插入和删除的数据结构。如图,支持进栈(\(push\))和出栈(\(pop\))两种操作。由于只能从一端进栈,一端出栈,所以任一元素,如上图中的\(s_2\),必须得在比它后进栈的\(s_3\sim s_n\)\(s_n\)\(s_{top}\))都出栈以后才能出栈,换句话说,先进来的元素必须等后进来的元素都出栈后才能出栈,故栈被称为“先进后出(\(FILO\))表”。

代码片段

进栈

进栈很简单,只需要将栈顶上移一格,然后在新的一格中放入元素即可。

  1. void push(int x){
  2. s[++top]=x;
  3. }

出栈

出栈也很简单,只需要将栈顶下移一格即可,且不需要替换,因为如果有元素进栈需占用此格时会将它替换。

  1. void pop(){
  2. top--;
  3. }

例题

P1739 表达式括号匹配

大意

给定一个以\(@\)结尾的表达式,判断其括号(只有“(”和“)”)是否匹配。
“(()())”,“((()))”匹配,但是“())”,“)()(”不匹配。

思路

如果只判断左右括号的数量的话显然是不行的,因为有很多数据显然过不去,如“)()(”,“())(()”等。
所以,这时候,我们就要使用栈了。逐个读入表达式,若为“(”则入栈(显然此时栈应该为\(char\)类型),若为“)”则判断,若栈顶为空或为“)”,即不为“(”,说明此时这个右半圆括号没有匹配到左半圆括号,说明不匹配。另外,若最后栈顶不为\(0\),说明还有剩下的括号没被匹配,也是不合法的。

代码
  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. using namespace std;
  5. char a[260],s[260];
  6. int top=0;
  7. int main(){
  8. cin>>a;
  9. for(int i=0;i<strlen(a);i++){
  10. if(a[i]=='('){
  11. s[++top]=a[i];
  12. }else if(a[i]==')'){
  13. if(a[i]==')'&&s[top]=='('){
  14. top--;
  15. }else{
  16. cout<<"NO";
  17. return 0;
  18. }
  19. }
  20. }
  21. if(top==0){
  22. cout<<"YES";
  23. }else{
  24. cout<<"NO";
  25. }
  26. return 0;
  27. }

P1449 后缀表达式求值

科普

逆波兰式(\(Reverse Polish notation\)\(RPN\),或逆波兰记法),也叫后缀表达式(将运算符写在操作数之后)。
一个表达式\(E\)的后缀形式可以如下定义:
(1)如果\(E\)是一个变量或常量,则E的后缀式是\(E\)本身。
(2)如果\(E\)\(E1 op E2\)形式的表达式,这里\(op\)是任何二元操作符,则E的后缀式为\(E1'E2' op\),这里\(E1'\)\(E2'\)分别为\(E1\)\(E2\)的后缀式。
(3)如果\(E\)\((E1)\)形式的表达式,则\(E1\)的后缀式就是\(E\)的后缀式。
如:我们平时写\(a+b\),这是中缀表达式,写成后缀表达式就是:\(ab+\)
\((a+b)*c-(a+b)/e\)的后缀表达式为:
\((a+b)*c-(a+b)/e\)
\(((a+b)*c)((a+b)/e)-\)
\(((a+b)c*)((a+b)e/)-\)
\((ab+c*)(ab+e/)-\)
\(ab+c*ab+e/-\)

这里用树解释上述样例:
首先我们构建上述表达式的树,使得树的中序遍历为表达式(因为我们写的是中缀表达式。若想将前缀表达式,即波兰式 变为树,则需构建树让其前序遍历为表达式即可),那么,这棵树的后序遍历就是逆波兰式。

大意

给出后缀表达式,输出其值。

思路

一个个读入后缀表达式,遇到数字进栈,遇到符号计算栈顶和栈顶后一位的元素,最后输出栈顶即可。

代码
  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. using namespace std;
  5. int s[260],top=1;
  6. char x;
  7. int main(){
  8. while(x!='@'){
  9. x=getchar();
  10. if(x=='.'){
  11. top++;
  12. }else{
  13. if(x>='0'&&x<='9'){
  14. s[top]=s[top]*10+(x-'0');
  15. }
  16. if(x=='+'){
  17. top--;
  18. s[top-1]+=s[top];
  19. s[top]=0;
  20. }else if(x=='-'){
  21. top--;
  22. s[top-1]-=s[top];
  23. s[top]=0;
  24. }else if(x=='*'){
  25. top--;
  26. s[top-1]*=s[top];
  27. s[top]=0;
  28. }else if(x=='/'){
  29. top--;
  30. s[top-1]/=s[top];
  31. s[top]=0;
  32. }else if(x=='@'){
  33. break;
  34. }
  35. }
  36. }
  37. cout<<s[--top];
  38. return 0;
  39. }

队列

算法思路

栈是一端进和出的数据结构,对应地,队列是一端进、另一端出的数据结构。

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。 ——摘自 百度百科


与栈类似,不多赘述。

单调栈

算法思路

顾名思义,单调栈是一种栈内元素具有单调性的栈。也就是说,我们将数列内的元素入栈,且要让栈内元素单调递增或单调递减。维护这样一个栈也很简单(以下假设维护单调递增的单调栈),我们将栈内的元素\(s_i\)表示为数列\(a\)中的元素的下标,也就是说,当你在看到栈内有一个元素\(x\)时,它表示的不是数字\(x\),而是表示\(a\)数组中的元素\(a_x\),这一点需要特别注意。入栈时,如果栈顶元素表示的值大于加入的值(即\(a_{s_{top}}>a_x\)\(x\)表示加入的元素,显然为\(a\)数组中的元素下标),那么将\(top--\),即把栈顶踢出去。因为加进来的\(x\),即代表的\(a_x\),已经小于栈顶了,而我们要维护单调递增的栈,所以若\(x\)直接加入会破坏单调性,故要让栈顶出栈。等到所有的要踢出栈顶\(top\)都被踢出后(即现在的栈顶\(top\)符合\(a_{s_{top}}<a_x\)),就可以将\(x\)入栈了。
那么,单调栈有什么作用呢?显然,你可以维护一个单调递增的栈,通过按顺序一个个入栈序列中的元素,可以求出长度为\(n\)的序列\(a\)中的前\(k\)个元素的最小值(\(0<k\le n\))。因为栈是单调递增的,所以按顺序加入前\(k\)个元素后,栈顶一定是前\(k\)个数中的最小值。但是,你显然可以不用单调栈来解决这一个非常基础的问题。单调栈的主要作用之一就是求出序列中每个数右(或左)边第一个比它大(或者小)的数(详情请见例题1)。

例题

P5788 【模板】单调栈 & P2947 [USACO09MAR]Look Up S

大意

求出序列中每个数右边第一个比它大的数。

思路

维护一个单调递减的栈,那么,对于每一个元素,让它被踢出去的那个元素一定是第一个比它大的元素。(建议自己画图好好分析一下)。

代码
  1. #include<iostream>
  2. #include<cstdio>
  3. #define maxn 100005
  4. using namespace std;
  5. int n,a[maxn];
  6. int s[maxn],top=0;
  7. int ans[maxn];
  8. void push(int x){
  9. while(a[s[top]]<a[x]&&top>0){
  10. ans[s[top]]=x;
  11. top--;
  12. }
  13. s[++top]=x;
  14. }
  15. int main(){
  16. scanf("%d",&n);
  17. for(int i=1;i<=n;i++){
  18. scanf("%d",&a[i]);
  19. }
  20. s[++top]=1;
  21. for(int i=2;i<=n;i++){
  22. push(i);
  23. }
  24. for(int i=1;i<=n;i++){
  25. printf("%d ",ans[i]);
  26. }
  27. return 0;
  28. }

P1901 发射站

大意

\(n\)个发射站,每一个发射站有一个高度\(h_i\)和能量值\(v_i\),对于每个发射站\(i\),它左边和右边第一个比它高的发射站都可以接收到它发出的信号(即累计值加上\(v_i\)),求每个信号塔累计的能量值总和的最大值。

思路

同上两题,只不过要加两次。

代码
  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #define maxn 1000005
  5. using namespace std;
  6. int n,a[maxn],v[maxn];
  7. int s[maxn],top=0;
  8. int ans[maxn];
  9. void push(int x){
  10. while(a[s[top]]<a[x]&&top>0){
  11. ans[x]+=v[s[top]];
  12. top--;
  13. }
  14. s[++top]=x;
  15. }
  16. int main(){
  17. scanf("%d",&n);
  18. for(int i=1;i<=n;i++){
  19. scanf("%d%d",&a[i],&v[i]);
  20. }
  21. s[++top]=1;
  22. for(int i=2;i<=n;i++){
  23. push(i);
  24. }
  25. memset(s,0,sizeof(s));
  26. top=0;
  27. s[++top]=n;
  28. for(int i=n-1;i>=1;i--){
  29. push(i);
  30. }
  31. int mmax=-1;
  32. for(int i=1;i<=n;i++){
  33. mmax=max(mmax,ans[i]);
  34. }
  35. printf("%d ",mmax);
  36. return 0;
  37. }

P2422 良好的感觉

大意

有一长为\(n\)的序列\(a\),定义某区间\([l,r]\)的值\(comfort_{l,r} = \min\limits_{i=l}^{r}{a_i} \times \sum\limits_{i=l}^{r}{a_i}\),求\(max(comfort_{i,j})(0 < i\le j\le n)\)

思路

可以想象,枚举每个区间需要耗费大量的时间,这很可能会使我们\(TLE\)。我们不妨枚举\(min(i,j)\),显然我们只需要枚举\(n\)次,因为我们可以从\(1\)\(n\)枚举\(i\),计算以\(a_i\)为最小值的区间中的最大\(comfort_{l,r}\),因为\(a_i \ge 1\),所以我们只要让区间越大越好,所以我们需要枚举的区间即为从\(a_i\)开始,左右两边各延伸到离它最近的比它小的两个位置(不包括这两个比它小的值)(因为这样就可以保证区间内没有小于\(a_i\)的数,即\(a_i\)为区间内最小值,且区间最大),计算所有的\(a_i\)计算的区间的和乘\(a_i\)(即以\(a_i\)为最小值的最大\(comfort\)值)的最大值即为所求最大值。

代码
  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #define maxn 100005
  5. using namespace std;
  6. int n,a[maxn];
  7. long long pre[maxn];
  8. int le[maxn],ri[maxn];
  9. int s[maxn],top=0;
  10. void pushr(int x){
  11. while(a[s[top]]>a[x]&&top>=0){
  12. ri[s[top]]=x;
  13. top--;
  14. }
  15. s[++top]=x;
  16. }
  17. void pushl(int x){
  18. while(a[s[top]]>a[x]&&top>=0){
  19. le[s[top]]=x;
  20. top--;
  21. }
  22. s[++top]=x;
  23. }
  24. int main(){
  25. scanf("%d",&n);
  26. for(int i=1;i<=n;i++){
  27. scanf("%d",&a[i]);
  28. pre[i]=pre[i-1]+a[i];
  29. }
  30. s[++top]=1;
  31. for(int i=2;i<=n;i++){
  32. pushr(i);
  33. }
  34. memset(s,0,sizeof(s));
  35. top=0;
  36. s[++top]=n;
  37. for(int i=n-1;i>=1;i--){
  38. pushl(i);
  39. }
  40. for(int i=1;i<=n;i++){
  41. ri[i]=ri[i]==0?n+1:ri[i];
  42. }
  43. long long ans=-1;
  44. for(int i=1;i<=n;i++){
  45. ans=max(ans,(long long)(a[i]*(pre[ri[i]-1]-pre[le[i]])));
  46. }
  47. printf("%lld",ans);
  48. return 0;
  49. }

单调队列

算法思路

我们刚才讲到,单调栈可以算出一组数据中前\(k\)个数据中的最值(虽然不用单调栈更简单易懂),那么,如何计算一组数据中任意连续\(k\)个数据的最值呢(\(0<k\le n\))?这时,我们就要用到单调队列了(注:用线段树也可以)。
单调队列相当于单调栈,但是它在队头也可以进行删除操作(即\(head++\))。以上述题目(即例题\(1\))为例,我们只要控制这个单调队列的队头始终满足在所求范围内即可。更详细的思路见例题\(1\)

例题

P1886 滑动窗口 /【模板】单调队列

大意

有一串长\(n\)的序列,有一个长\(m\)的窗口从\(1\)\(n-m+1\)滑动(\(0<m<=n\)),求每滑动一次窗口的最值。

思路

模板题。
维护队列\(q\)(当然能用\(STL\)),(这里讲最大值的做法,最小值同理)每次加入一个元素,从队尾把小于此元素的从队尾出队(因为当前的元素已经比它大了,且窗口向右滑动,所以它必定不可能再一次成为最大值),从队首将不在范围内的元素从队首出队,这时队首即为最大值。
代码中,先将前\(k-1\)个元素入队,然后循环\(i\)\(k\le i\le n\)),枚举队尾,用\(push\)函数从队尾插入,同时从队头删去\(i-k+1\)之前的元素,即已经不在滑动窗口内的元素,输出队头。

代码
  1. #include<iostream>
  2. #include<cstdio>
  3. #define maxn 1000005
  4. using namespace std;
  5. int n,k;
  6. int a[maxn];
  7. int q1[maxn],q2[maxn],h1=1,t1=0,h2=1,t2=0;//q1维护最大值,q2维护最小值
  8. int ans1[maxn],ans2[maxn];
  9. void push1(int x,int l){
  10. while(t1>=h1&&a[x]>a[q1[t1]]){
  11. t1--;
  12. }
  13. q1[++t1]=x;
  14. while(t1>=h1&&q1[h1]<l){
  15. h1++;
  16. }
  17. }
  18. void push2(int x,int l){
  19. while(t2>=h2&&a[x]<a[q2[t2]]){
  20. t2--;
  21. }
  22. q2[++t2]=x;
  23. while(t2>=h2&&q2[h2]<l){
  24. h2++;
  25. }
  26. }
  27. int main(){
  28. scanf("%d%d",&n,&k);
  29. for(int i=1;i<=n;i++){
  30. scanf("%d",&a[i]);
  31. }
  32. q1[++t1]=1;
  33. q2[++t2]=1;
  34. for(int i=2;i<=k;i++){
  35. push1(i,-1);
  36. push2(i,-1);
  37. }
  38. ans1[1]=q1[h1];
  39. ans2[1]=q2[h2];
  40. for(int i=k+1;i<=n;i++){
  41. push1(i,i-k+1);
  42. push2(i,i-k+1);
  43. ans1[i-k+1]=q1[h1];
  44. ans2[i-k+1]=q2[h2];
  45. }
  46. for(int i=1;i<=n-k+1;i++){
  47. printf("%d ",a[ans2[i]]);
  48. }
  49. printf("\n");
  50. for(int i=1;i<=n-k+1;i++){
  51. printf("%d ",a[ans1[i]]);
  52. }
  53. return 0;
  54. }

双倍经验(只需求最大值):P2032 扫描

P2629 好消息,坏消息

大意

给定一个环,问从任意元素开始累加,有多少种情况使得累加时总和\(tot\)恒大于等于\(0\)

思路

断环成链,维护单调队列和前缀和\(pre_i\),若某一长度为\(n\)的序列\(i\sim i+n-1\)的最小值减\(pre_{i-1}\)大于零的话即可行。

代码
  1. #include<iostream>
  2. #include<cstdio>
  3. #define maxn 1000005
  4. using namespace std;
  5. int n,k;
  6. int a[maxn*2],pre[maxn*2];
  7. int q[maxn],h=1,t=0;
  8. int ans[maxn];
  9. void push(int x,int l){
  10. while(t>=h&&pre[x]<pre[q[t]]){
  11. t--;
  12. }
  13. q[++t]=x;
  14. while(t>=h&&q[h]<l){
  15. h++;
  16. }
  17. }
  18. int main(){
  19. scanf("%d",&n);
  20. k=n;
  21. n*=2;
  22. for(int i=1;i<=k;i++){
  23. scanf("%d",&a[i]);
  24. a[i+k]=a[i];
  25. }
  26. for(int i=1;i<=n;i++){
  27. pre[i]=pre[i-1]+a[i];
  28. }
  29. q[++t]=1;
  30. for(int i=2;i<=k;i++){
  31. push(i,-1);
  32. }
  33. ans[1]=q[h];
  34. for(int i=k+1;i<n;i++){
  35. push(i,i-k+1);
  36. ans[i-k+1]=q[h];
  37. }
  38. int tot=0;
  39. for(int i=1;i<=n-k;i++){
  40. if(pre[ans[i]]-pre[i-1]>=0){
  41. tot++;
  42. }
  43. }
  44. printf("%d",tot);
  45. return 0;
  46. }

单调队列优化DP

例题

P1725 琪露诺

大意

有一串长度为\(n+1\)的序列\(a\),从\(0\)出发,在某个节点\(i\)能走到\(i+l\sim i+r\)的任意位置,\(tot\)累加当前位置的\(a_i\),求离开时的最大\(tot\)值(\(i+r>n\)代表从\(i\)节点能离开)。

思路

显然此题暴力代码复杂度为\(O(N^2)\)\(f_i=\max\limits_{j=max(0,i-r)}^{i-l}{f_j}(l<=i<=n)\),那么,我们只要开一个单调队列求前文的\(max(f_j)\)即可。

代码

(细节很多)

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #define maxn 1000005
  5. #define INF 0x3f
  6. using namespace std;
  7. int n,l,r;
  8. int a[maxn];
  9. int q[maxn],h=1,t=0,maxx[maxn];
  10. int f[maxn];
  11. void push(int x,int l){
  12. while(f[x]>f[q[t]]&&h<=t){
  13. t--;
  14. }
  15. while(q[h]<l&&h<=t){
  16. h++;
  17. }
  18. q[++t]=x;
  19. }
  20. void clear(int l){
  21. while(q[h]<l&&h<=t){
  22. h++;
  23. }
  24. }
  25. int main(){
  26. scanf("%d%d%d",&n,&l,&r);
  27. memset(f,0x80,sizeof(f));
  28. memset(q,-1,sizeof(q));
  29. for(int i=0;i<=n;i++){
  30. scanf("%d",&a[i]);
  31. }
  32. f[0]=0;
  33. push(0,-1);
  34. for(int i=l;i<=n;i++){
  35. if(i-l>=l){
  36. push(i-l,max(0,i-r));
  37. }else{
  38. clear(max(0,i-r));
  39. }
  40. if(q[h]==-1){
  41. f[i]=f[maxn-1]+a[i];
  42. }else{
  43. f[i]=f[q[h]]+a[i];
  44. }
  45. // for(int j=i-l;j>=max(0,i-r);j--){
  46. // f[i]=max(f[i],f[j]+a[i]);
  47. // }
  48. }
  49. int ans=-INF;
  50. for(int i=n;i>=n-r+1;i--){
  51. ans=max(ans,f[i]);
  52. }
  53. printf("%d",ans);
  54. return 0;
  55. }

P2627 Mowing the Lawn G

大意

有一个长度为\(n\)的序列,选出若干个元素,使得选出的元素没有连续超过\(k\)个的(\(0<k<=n\)),求选出元素的和的最大值。

思路

选出的数的和的最大值可以转换成删去的数的和的最小值。

代码
  1. #include<iostream>
  2. #include<cstdio>
  3. #define maxn 100005
  4. using namespace std;
  5. long long n,k,a[maxn],f[maxn];
  6. long long q[maxn],h=0,t=0;
  7. long long tot=0;
  8. void push(long long x,long long l){
  9. while(f[x]<f[q[t]]&&h<=t){
  10. t--;
  11. }
  12. while(q[h]<l&&h<=t){
  13. h++;
  14. }
  15. q[++t]=x;
  16. }
  17. int main(){
  18. scanf("%lld%lld",&n,&k);
  19. for(long long i=1;i<=n;i++){
  20. scanf("%lld",&a[i]);
  21. tot+=a[i];
  22. }
  23. for(int i=1;i<=n;i++){
  24. f[i]=f[q[h]]+a[i];
  25. push(i,i-k);
  26. }
  27. long long mmin=f[n-k];
  28. for(long long i=n-k+1;i<=n;i++){
  29. mmin=min(f[i],mmin);
  30. }
  31. printf("%lld",tot-mmin);
  32. return 0;
  33. }
  34. /*
  35. 5 4
  36. 1 2 3 4 5
  37. */

双倍经验:P2034 扫描

qzez1926 玉米实验

大意

给定一个\(n*n\)的矩阵\(a\),有\(t\)次询问,每次询问以\((x_i,y_i)\)为左上角、长宽均为\(k\)的矩阵中,最大值与最小值的差值是多少。

思路

首先,我们还是显然能得出暴力的代码:
\(ans_i = \max\limits_{x=x_i , y=y_i}^{x_i+k-1 , y_i+k-1}{a_{{x},{y}}} - \min\limits_{x=x_i , y=y_i}^{x_i+k-1 , y_i+k-1}{a_{{x},{y}}}\)
然后,由于\(k\)是给定的,这让我们想到可以用单调队列优化\(max(a_{{x},{y}})\)\(min(a_{{x},{y}})\),即预处理矩阵\(a\),算出矩阵每一行的长\(k\)的滑动窗口中的最值,询问时直接查询子矩阵第一列的最值即可。

代码
  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #define maxn 1005
  5. #define INF 99999999
  6. using namespace std;
  7. int n,k,t,x,y;
  8. int a1[maxn][maxn],a2[maxn][maxn];
  9. int q1[maxn],q2[maxn],h1=1,t1=0,h2=1,t2=0;
  10. int ans1[maxn][maxn],ans2[maxn][maxn];
  11. void push1(int i,int x,int l){//找最小值
  12. while(a1[i][x]<a1[i][q1[t1]]&&h1<=t1){
  13. t1--;
  14. }
  15. q1[++t1]=x;
  16. while(q1[h1]<l&&h1<=t1){
  17. h1++;
  18. }
  19. }
  20. void push2(int i,int x,int l){//找最大值
  21. while(a2[i][x]>a2[i][q2[t2]]&&h2<=t2){
  22. t2--;
  23. }
  24. q2[++t2]=x;
  25. while(q2[h2]<l&&h2<=t2){
  26. h2++;
  27. }
  28. }
  29. void doit(int i){
  30. memset(q1,0,sizeof(q1));
  31. memset(q2,0,sizeof(q2));
  32. h1=1;t1=0;h2=1;t2=0;
  33. q1[++t1]=1;q2[++t2]=1;
  34. for(int j=2;j<k;j++){
  35. push1(i,j,-1);
  36. push2(i,j,-1);
  37. }
  38. for(int j=k;j<=n+k;j++){
  39. push1(i,j,j-k+1);
  40. push2(i,j,j-k+1);
  41. ans1[i][j-k+1]=q1[h1];
  42. ans2[i][j-k+1]=q2[h2];
  43. }
  44. }
  45. int main(){
  46. memset(a1,0x3f,sizeof(a1));
  47. scanf("%d%d%d",&n,&k,&t);
  48. for(int i=1;i<=n;i++){
  49. for(int j=1;j<=n;j++){
  50. scanf("%d",&a1[i][j]);
  51. a2[i][j]=a1[i][j];
  52. }
  53. }
  54. for(int i=1;i<=n;i++){
  55. doit(i);
  56. }
  57. // printf("\n");
  58. // for(int i=1;i<=n;i++){
  59. // for(int j=1;j<=n;j++){
  60. // printf("%d ",a2[i][ans2[i][j]]);
  61. // }
  62. // printf("\n");
  63. // }
  64. // printf("\n");
  65. while(t--){
  66. scanf("%d%d",&x,&y);
  67. int mmax=-1,mmin=INF;
  68. for(int i=0;i<k;i++){
  69. mmax=max(mmax,a2[x+i][ans2[x+i][y]]);
  70. mmin=min(mmin,a1[x+i][ans1[x+i][y]]);
  71. }
  72. printf("%d\n",mmax-mmin);
  73. }
  74. return 0;
  75. }
  76. /*
  77. 5 3 2
  78. 5 1 2 6 3
  79. 1 3 5 2 7
  80. 7 2 4 6 1
  81. 9 9 8 6 5
  82. 0 6 9 3 9
  83. 2 1
  84. 1 2
  85. */

原文链接:http://www.cnblogs.com/qzhwlzy/p/14838637.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号