方法:找每个点的左边的比它小的数的所在的位置,然后如果两点的左边比它小的数的位置不同,那么肯定这一位就不能取了。
证明:本质来说这道题目也许似乎可能找到规律就好了,因为在[1,p]的这个区域里面去找任意值的话,其实只要找到前面的一个状态就好了,比如说找【l,r】这个区域是否相等,就只要找到a,b两个数组里面a[i],b[i]是否会小于【l,r-1】的最小值,如果都不小于的话那么[l,r]=[l,r-1],还有如果都小于最小值[l,r]就会等于r,如果有且仅有一个的话,那么该点就不可以取,答案则为r-1
那么其实就是找到每个点的左边最近的比他小的值的点相同的话就可以取,不相同的就不可以取。
对于i点来说,如果i的点的左边最近的比他小的值的点a数组和b数组相同,假设那个点为f,那么再【1,f】这个区间内的任意取法都不会改变,因为新加入的点a[i],b[i]不会影响f之前的数据,那么对于[f,i]来看首先[f,i]是相同的等于f.因为我们找的f点是左边最近的比他小的值的点。那么在区间【f+1,i】点来说a[i],b[i]就是它的最小值,也就是说在[f+1,i]任意取区间,它的值都等于i,因为a[i]和b[i]在这段区间里面就是最小的。而对于[f,i-1]的这段任意取值里面不会改变原来的状态
实现:
用单调栈来存点之前最小的元素,如果这个点小于栈顶元素的话,那么就把所有元素都弹出来,。最后面判断两个栈元素的大小是否相同,以此来判断最小的值的索引是之前没有变还是新加入的元素。
#include<stdio.h> #include<string.h> #include<math.h> #include<algorithm> #include <stack> using namespace std; int n,m; int main() { int i,j; stack<int> s1,s2; int a[100005],b[100005]; while(scanf("%d",&n)!=EOF){ for(i=1;i<=n;i++){ scanf("%d",&a[i]); } for(i=1;i<=n;i++){ scanf("%d",&b[i]); } //清空元素 while(!s1.empty()){ s1.pop(); } while(!s2.empty()){ s2.pop(); } j=1; //对于第一个元素来说无需证明 s1.push(a[1]); s2.push(b[1]); while(j<n){ //保持单调栈 while(!s1.empty()&&s1.top()>a[j+1]){ s1.pop(); } while(!s2.empty()&&s2.top()>b[j+1]){ s2.pop(); } if(s1.empty()||s1.top()<a[j+1]){ s1.push(a[j+1]); } if(s2.empty()||s2.top()<b[j+1]){ s2.push(b[j+1]); } //如果大小不同了就代表弹出去的元素的数目不同,出现这种的可能性就是其中有一个 s.top()<a[j+1),而另外一个就相反了 if(s2.size()!=s1.size()){ break; } j++; } printf("%d\n",j); } }