今天收获还是有点的,做了几道题,也算是在做题的过程进一步了解了一下栈的用途了吧。
比如这道题:
Farmer John的奶牛在风中凌乱了它们的发型……
每只奶牛都有一个身高hi(1 ≤ hi ≤ 1,000,000,000),现在在这里有一排全部面向右方的奶牛,一共有N只(1 ≤ N ≤ 80,000)。对于奶牛i来说,如果奶牛i+1,i+2,……,N这些奶牛的身高严格小于奶牛i,则奶牛i可以看到它们凌乱的发型。
比如下面这个例子:
* * * * = *
= * * * = *
= * - * = * 奶牛面向这边-->
= * = * = *
= - = = = *
= = = = = =
1 2 3 4 5 6
('*'表示空的,这是译者为了格式特意弄的,原题没有)
令ci表示第i只奶牛能够看到的发型数量,请计算c1 + c2 + c3 + … + cN的值
对于上面这个例子,答案为3 + 0 + 1 + 0 + 1 + 0=5。
输入
第一行:奶牛数量N
第二到N+1行:第i+1行输入奶牛i的身高
输出
第一行:一个整数即c1到cN的和
样例输入
6
10
3
7
4
12
2
样例输出
5
最开始的想法就是暴力枚举,即每到一个数就往后找,直到找到比他大的数停下,看本题的数据就知道时间肯定会超限。于是我们可以考虑用一种叫单调栈的方法去做(我也是在网上找了找)。这题就是构建递减栈去做。保证栈的数据都是单调递减的,用sum去记录每次递减的个数,举个例子。比如样例,第一次将6入栈,第二次发现10比6大,于是6弹出栈,10入栈;第三次3入栈,此时栈中有10,3。满足递减顺序(3对10有贡献),于是sum+1,接下来7入栈将3弹出此时栈中有10,7.满足递减顺序,sum+1(7对10有贡献),第四次4入栈,此时栈中有10,7,4,sum+2(4对10和7有贡献),依次类推。其实我们不难发现每次sum要加上的数就是栈中的数据的个数减1。规律找出,话不多说,上代码!!!
#include<stdio.h>
int main()
{
int a[80010],stack[80010];
int i,n,top=0;
long long sum=0;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
for(i=1;i<=n;i++)
{
if(top==0||stack[top]>a[i]){
sum += top;
stack[++top] = a[i];
}
else
{
while(stack[top]<=a[i]&&top>0)
top--;
sum += top;
stack[++top] = a[i];
}
}
printf("%lld",sum);
return 0;
}
注意我上面是先加上递减栈个数,再把当前数入栈的,所以不用-1。
关于思考和总结:经过改进后,可以看到时间复杂度已经将到了O(n),有人可能会疑惑,明明for循环里还有个while循环为什么是O(n)呢?我们可以知道while循环的操作就是将数弹出栈的操作,n个数最多只能弹n-1次,那么复杂度还是O(n)(注意n前的系数能忽略)。
有关栈的知识我还是在做题目中理解的,也初步见识到了栈的便利之处。(初步有了个以空间换时间的思想)