单调栈总结

最近刚学了单调栈,又做了几道题。。。写个总结。。。

T1:Bad Hair Day

Description

Some of Farmer John's N cows (1 ≤ N ≤ 80,000) are having a bad hair day! Since each cow is self-conscious about her messy hairstyle, FJ wants to count the number of other cows that can see the top of other cows' heads.

Each cow i has a specified height hi (1 ≤ hi ≤ 1,000,000,000) and is standing in a line of cows all facing east (to the right in our diagrams). Therefore, cow i can see the tops of the heads of cows in front of her (namely cows i+1, i+2, and so on), for as long as these cows are strictly shorter than cow i.

Consider this example:

        =
=       =
=   -   =         Cows facing right -->
=   =   =
= - = = =
= = = = = =
1 2 3 4 5 6 

Cow#1 can see the hairstyle of cows #2, 3, 4
Cow#2 can see no cow's hairstyle
Cow#3 can see the hairstyle of cow #4
Cow#4 can see no cow's hairstyle
Cow#5 can see the hairstyle of cow 6
Cow#6 can see no cows at all!

Let ci denote the number of cows whose hairstyle is visible from cow i; please compute the sum of c1 through cN.For this example, the desired is answer 3 + 0 + 1 + 0 + 1 + 0 = 5.

Input

Line 1: The number of cows, N
Lines 2..N+1: Line i+1 contains a single integer that is the height of cow i.

Output

Line 1: A single integer that is the sum of c1 through cN.

大意:给你一排奶牛,每只奶牛向右看,只能看到比自己矮的,并且直到有一个比自己高的,在它后面的无论高矮就都看不到了。。

画个图吧。。。

1可以看到2 3 4 ,2谁也看不到,3可以看到4,4看不到,5可以看到6,6看不到,所以答案是5

我们想想,如果要暴力的话。。。从1 扫到5,因为5的高度比1高,所以停止了扫描。然后2开始扫描,我们就会发现,无论如何,2都不可能比5高,因为从1 扫描到比他高的停止,1比2高,5比1高,所以5比2高。。。所以2扫描停止的位置会在3---5之间。。。可是我们在1的时候已经扫描过了一次,能不能只扫描一次就算出所有的,这样就省了很多时间。。。其实是可以的。。。每个数第一次遇到了比自己大的就可以知道它能看到多少,比如2遇到3,那么2---3之间的都是能看到的,所以2能看到3-2-1=0个。如果我们用一个容器去装他们的话,一个已经计算出答案的数,我就可以让他出来了,他放在容器里也没有多大价值了。也就是这个2没有价值了。。所以他就不用在这个容器里占空间和时间了。。。这样看来,我们维护的是一个单调下降的序列。这不是就是单调栈了。。。这次准备放进去的牛的高度如果比栈顶的要高,那就通过下标相减求出答案,然后把它出栈,直到栈空或者栈顶比当前牛的高度要高的时候就把牛的高度连同下标放进栈里面。。。

#include<iostream>
#include<cstdio>
#include<stack>
#define max_n 80500
using namespace std;
//ps:码风不好。。。。嘻嘻嘻
int n,x;
struct index{//最近有点沉迷魔禁,茵蒂克丝。。。嘻嘻嘻。。。。
	int id,num;//id下标,num是高度
}; 
stack <index> q;

int main()
{
	long long ans=0;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>x;
		while(!q.empty()&&q.top().num<=x) ans+=i-q.top().id-1,q.pop();
		index o;
		o.id=i,o.num=x;
		q.push(o);		
	}
	if(!q.empty())//还有一些没有计算完的在栈里头。。。
	{	
		int cnt=q.top().id;
		q.pop();
		while(!q.empty())
		{
			ans+=cnt-q.top().id;
			q.pop();
		}
	} 
	cout<<ans<<"\n";
	return 0;
}

T2Feel Good

Description

Bill is developing a new mathematical theory for human emotions. His recent investigations are dedicated to studying how good or bad days influent people's memories about some period of life. 

A new idea Bill has recently developed assigns a non-negative integer value to each day of human life. 

Bill calls this value the emotional value of the day. The greater the emotional value is, the better the daywas. Bill suggests that the value of some period of human life is proportional to the sum of the emotional values of the days in the given period, multiplied by the smallest emotional value of the day in it. This schema reflects that good on average period can be greatly spoiled by one very bad day. 

Now Bill is planning to investigate his own life and find the period of his life that had the greatest value. Help him to do so.

Input

The first line of the input contains n - the number of days of Bill's life he is planning to investigate(1 <= n <= 100 000). The rest of the file contains n integer numbers a1, a2, ... an ranging from 0 to 106 - the emotional values of the days. Numbers are separated by spaces and/or line breaks.

Output

Print the greatest value of some period of Bill's life in the first line. And on the second line print two numbers l and r such that the period from l-th to r-th day of Bill's life(inclusive) has the greatest possible value. If there are multiple periods with the greatest possible value,then print any one of them.

Sample Input

6
3 1 6 4 5 2

Sample Output

60
3 5

大意:求在所有子区间中,一个区间的和乘上区间最小值。。。。这样的结果最大是多少,并且随便输出一种方案(有spj)。。。emmmm。。。其实我也没想多少。。。可能是刚做了一道题的缘故。。。https://blog.csdn.net/qq_35819724/article/details/86647175可以看看这篇文章的最后一点。。。拓展:单调栈。。。

其实是差不多的,可以把题目转换成这样:把每个数具想成一个正方形,3就是边长为3的正方形,一堆正方形挨在一起,在里头找一个面积最大的矩形(最大的矩形符合哪些特征?底边长也就是几个正方形的边长和,高也就是这几个正方形里最矮的那个的高,这也就是题意要求啊),这样就是等价了。。。也就变成上面那道题。。。只是找方案有点烦。。。要记录一个l,r合并后的矩形的l,r会改变。。。很难讲啊。。。还是看代码吧。。。

#include<iostream>
#include<cstdio>
#include<stack>
using namespace std;

int t,al,ar;
long long ans=-1;
struct st{
	long long h,w;
	int l,r;
}; 
stack<st> q;

int main()
{
	scanf("%d",&t);
	ans=-1;
	for(int i=1;i<=t;i++)
	{
		long long x;
		scanf("%lld",&x);
		if(q.empty())
		{
			st u;
			u.h=x;
			u.w=x;
			u.l=i;//记录这个矩形的编号
			u.r=i;
			q.push(u);
		}
		else if(x>q.top().h)
		{
			st u;
			u.h=x;
			u.w=x;	
			u.l=i;
			u.r=i;
			q.push(u);
		}
		else 
		{
			long long with=0;
			int rt;
			if(!q.empty()) rt=q.top().r;
			int mark=i;
			while(!q.empty()&&q.top().h>=x)
			{
				with+=q.top().w;
				if(ans<q.top().h*with)
				{
					ans=q.top().h*with;
					al=q.top().l;
					ar=rt;
				}
				mark=q.top().l;//因为要合并这一段,然后再放栈里,所以要记录这一段最左边的编号
                              //从而方便计算答案。。。
				q.pop();
			}
			st u;
			u.h=x;
			u.w=with+x;	
			u.l=mark;
			u.r=i;
			q.push(u);
		}
	}
	long long len=0;
	int rt;
	if(!q.empty()) rt=q.top().r;
	while(!q.empty())
	{
		len+=q.top().w;
		if(ans<len*q.top().h) 
		{
			ans=len*q.top().h;
			al=q.top().l;
			ar=rt;
		}
		q.pop();
	}
	printf("%lld\n%d %d\n",ans,al,ar);
	return 0;
 } 

T3:Largest Submatrix of All 1’s

Description

Given a m-by-n (0,1)-matrix, of all its submatrices of all 1’s which is the largest? By largest we mean that the submatrix has the most elements.

Input

The input contains multiple test cases. Each test case begins with m and n (1 ≤ mn ≤ 2000) on line. Then come the elements of a (0,1)-matrix in row-major order on m lines each with n numbers. The input ends once EOF is met.

Output

For each test case, output one line containing the number of elements of the largest submatrix of all 1’s. If the given matrix is of all 0’s, output 0.

Sample Input

2 2
0 0
0 0
4 4
0 0 0 0
0 1 1 0
0 1 1 0
0 0 0 0

Sample Output

0
4

大意:找一个面积最大的全都是1的矩阵。。。其实都是举一反三的。。。虽然这个乱了一点。。。虽然他的布局有些杂乱,但是还是差不多的。。。只要我们把每行当作地面,然后把1当作箱子,如果1浮空的话,就没必要计算了,因为你是一行一行的算,之前已经算过了。。。然后又把它转化成了那个矩形求面积问题。。。只是要与处理一下。。。。

#include<iostream>
#include<cstdio>
#include<stack>
using namespace std;

int n,m,sum[2001][2001];
bool map_[2001][2001];
long long ans=-1;
struct st{
	long long h,w;
}; 
stack<st> q;

int main()
{
	while(cin>>n>>m)
	{
		ans=-1;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
			{
				scanf("%d",&map_[i][j]);
				if(map_[i][j]==1) sum[i][j]=sum[i-1][j]+1;
				else sum[i][j]=0;//预处理,把上下连在一起的箱子当成一个立起来的矩形
			}
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				if(q.empty())
				{
					st u;
					u.h=sum[i][j];
					u.w=1;
					q.push(u);
				}
				else if(sum[i][j]>q.top().h)
				{
					st u;
					u.h=sum[i][j];	
					u.w=1;
					q.push(u);
				}
				else 
				{
					long long with=0;
					while(!q.empty()&&q.top().h>=sum[i][j])
					{
						with+=q.top().w;
						ans=max(ans,q.top().h*with);
						q.pop();
					}
					st u;
					u.h=sum[i][j];
					u.w=with+1;	
					q.push(u);
				}
			}
			long long len=0;
			while(!q.empty())
			{
				len+=q.top().w;
				ans=max(ans,len*q.top().h);
				q.pop();
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
 } 

总结:单调栈虽然没有单调队列那么常用,但是他的思想经常用到一些地方。。。一定要把思想掌握。。。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单调栈是一种常用的数据结构,用于解决一类特定的问题,其中最常见的问题是找到数组中每个元素的下一个更大或更小的元素。在Codeforces编程竞赛中,单调栈经常被用于解决一些与数组相关的问题。 下面是单调栈的一般思路: 1. 创建一个空栈。 2. 从左到右遍历数组元素。 3. 对于每个元素,将其与栈顶元素进行比较。 - 如果当前元素小于等于栈顶元素,则将当前元素入栈。 - 如果当前元素大于栈顶元素,则将栈顶元素弹出,并将当前元素入栈。 4. 重复步骤3,直到遍历完所有元素。 这样,最后剩下的栈中元素就是没有下一个更大或更小元素的元素。在使用单调栈求解具体问题时,我们可以根据需要进行一些特定的操作。 例如,如果要找到一个数组中每个元素的下一个更大的元素,可以使用单调递减栈。具体操作如下: 1. 创建一个空栈和一个空结果数组。 2. 从左到右遍历数组元素。 3. 对于每个元素,将其与栈顶元素进行比较。 - 如果当前元素小于等于栈顶元素,则将当前元素入栈。 - 如果当前元素大于栈顶元素,则将栈顶元素弹出,并将其在结果数组中的位置记录为当前元素的下一个更大元素的索引。 4. 将当前元素入栈。 5. 重复步骤3和4,直到遍历完所有元素。 6. 结果数组中没有下一个更大元素的位置,可以设置为-1。 以下是一个使用单调递减栈求解下一个更大元素问题的示例代码: ```cpp #include <iostream> #include <stack> #include <vector> std::vector<int> nextGreaterElement(std::vector<int>& nums) { int n = nums.size(); std::vector<int> result(n, -1); std::stack<int> stack; for (int i = 0; i < n; i++) { while (!stack.empty() && nums[i] > nums[stack.top()]) { result[stack.top()] = i; stack.pop(); } stack.push(i); } return result; } int main() { std::vector<int> nums = {1,3, 2, 4, 5, 1}; std::vector<int> result = nextGreaterElement(nums); for (int i = 0; i < result.size(); i++) { std::cout << "Next greater element for " << nums[i] << ": "; if (result[i] != -1) { std::cout << nums[result[i]]; } else { std::cout << "None"; } std::cout << std::endl; } return 0; } ``` 以上代码将输出: ``` Next greater element for 1: 3 Next greater element for 3: 4 Next greater element for 2: 4 Next greater element for 4: 5 Next greater element for 5: None Next greater element for 1: None ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值