poj_3250 单调栈
题目大意
N头牛排成一列,每头牛A都能看到它前面比它矮的牛i,若前面有一个比他高的牛X,则X之前的牛j,A都无法看到。给出N头牛的高度,求出这N头牛可以看到牛的数目的总数。
题目分析
画图之后,可以很容易看出是典型的单调栈问题。单调栈问题中栈元素一般有两个属性:一是牛的索引位置,二是牛的高度。每次得到一个牛index的高度height之后,都和栈顶元素进行比较,若height > stack[top].height 则直接将(新牛的高度height,新牛的索引index)入栈,否则,弹栈,直到栈顶元素stack[top].height < height,再将(新牛的高度height,新牛的索引index)入栈。
在牛A被出栈的时候,可以获得牛A能够看到的牛的个数(即新入栈的牛B的索引index_b - 牛A的索引index_a)。最后记得清空栈元素来获得所有值。
实现(c++)
可以不使用单调栈
#include<stdio.h> #include<iostream> #include<string> #include<string.h> #include<vector> #include<stack> #include<queue> #include<deque> #include<list> #include<set> #include<set> #include<map> #include<functional> #include<algorithm> using namespace std; const int kMax = 80005; //height 数组 int height[kMax]; //从i点向左看,第一个高度等于或者大于height[i]的数组下标 int higher_index[kMax]; long long int solve(int n){ int j; long long result = 0; for (int i = 0; i < n; i++){ j = i - 1; while (j >= 0 && height[j] < height[i]){ j = higher_index[j]; } higher_index[i] = j; result += (i - j - 1); } return result; } int main(){ int n; scanf("%d", &n); //题目要求从左向右看,将数组颠倒,然后换个方向,从右向左看,结果是一样的 for (int i = n - 1; i >= 0; i--){ scanf("%d", &height[i]); } long long int result = solve(n); printf("%lld\n", result); return 0; }
使用单调栈
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #define MAX_COW_NUM 80005 int st[MAX_COW_NUM][2]; int gCanSeeNum[MAX_COW_NUM]; int main(){ int n; scanf("%d", &n); int height, top = -1; long long sum = 0; for (int i = 0; i < n; i++){ scanf("%d", &height); while (top >= 0 && st[top][1] <= height){ gCanSeeNum[st[top][0]] = i - st[top][0] - 1; sum += gCanSeeNum[st[top][0]]; top--; } top++; st[top][0] = i; st[top][1] = height; } while (top >= 0){ gCanSeeNum[st[top][0]] = n - st[top][0] - 1; sum += gCanSeeNum[st[top][0]]; top--; } printf("%lld\n", sum); return 0; }
使用单调栈2
#include<stdio.h> #include<iostream> #include<string> #include<string.h> #include<vector> #include<stack> #include<queue> #include<deque> #include<list> #include<set> #include<set> #include<map> #include<functional> #include<algorithm> using namespace std; const int kMax = 80005; //height 数组 int height[kMax]; int mon_stack[kMax]; long long int solve(int n){ int top = -1; long long result = 0; for (int i = 0; i < n; i++){ while (top >= 0 && height[mon_stack[top]] < height[i]){ top--; } if (top < 0) result += i; else result += (i - mon_stack[top] - 1); mon_stack[++top] = i; } return result; } int main(){ int n; scanf("%d", &n); //题目要求从左向右看,将数组颠倒,然后换个方向,从右向左看,结果是一样的 for (int i = n - 1; i >= 0; i--){ scanf("%d", &height[i]); } long long int result = solve(n); printf("%lld\n", result); return 0; }