AcWing 152. 城市游戏 单调栈,栈解法,悬线法

原题链接:https://www.acwing.com/problem/content/description/154/
题目描述
有一天,小猫rainbow和freda来到了湘西张家界的天门山玉蟾宫,玉蟾宫宫主蓝兔盛情地款待了它们,并赐予它们一片土地。

这片土地被分成N*M个格子,每个格子里写着’R’或者’F’,R代表这块土地被赐予了rainbow,F代表这块土地被赐予了freda。

现在freda要在这里卖萌。。。它要找一块矩形土地,要求这片土地都标着’F’并且面积最大。

但是rainbow和freda的OI水平都弱爆了,找不出这块土地,而蓝兔也想看freda卖萌(她显然是不会编程的……),所以它们决定,如果你找到的土地面积为S,它们将给你3*S两银子。

输入格式
第一行包括两个整数N,M,表示矩形土地有N行M列。

接下来N行,每行M个用空格隔开的字符’F’或’R’,描述了矩形土地。

每行末尾没有多余空格。

输出格式
输出一个整数,表示你能得到多少银子,即(3*最大’F’矩形土地面积)的值。

数据范围
1≤N,M≤1000
样例
输入样例:
5 6
R F F F F F
F F F F F F
R R R F F F
F F F F F F
F F F F F F
输出样例:
45

思路:要由F组成最大的矩阵,等价于求直方图的最大面积。
 先统计第一行到每一行的连续矩形个数。如果下一行对应为'F',那么
 h[i][j]=h[i-1][j]+1;否则,h[i][j]=0;
 接下来枚举每一行,求直方图最大面积。 

求直方图最大面积模板:

a[n+1]=p=0;//增加一个高度为0的矩形,以免在扫描结束后还有剩余矩形
for(int i=1;i<=n;i++){
	if(a[i]>s[p]){//当前矩形比栈顶矩形高,直接入栈,并设置宽度为1
		s[++p]=a[i];
		w[p]=1;
	}
	else{
		int width=0;
		while(s[p]>a[i]){
			width+=w[p];//累计被弹出的矩形宽度之和
			ans=max(ans,width*s[p]);//不断更新面积最大值
			p--;//弹出
		}
		s[++p]=a[i],w[p]=width+1;//整个出栈过程结束后,把一个高度为当前矩形高度,宽度为累计值的新矩形入栈
	}
}

代码如下:

(1)单调栈解法:

#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int h[N][N];//存储F的数量 
int n,m,s[N],w[N];//s为栈,w为宽度 
int get(int a[]){//对于每一行 
	int p=0,ans=0;
	a[m+1]=0; 
	for(int i=1;i<=m+1;i++){
		if(a[i]>s[p]){//如果比栈顶元素高,直接进栈 
			s[++p]=a[i];
			w[p]=1;//宽度为1 
		}
		else{
			int width=0;
			while(s[p]>a[i]){
				width+=w[p];//累计被弹出的矩形之和 
				ans=max(ans,width*s[p]);//
				p--; 
			}
			s[++p]=a[i],w[p]=width+1;
			//把一个高度为当前矩形高度,宽度为累计值的新矩形入栈 
		}
	}
	return ans;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			char ch;
			cin>>ch;
			if(ch=='F') h[i][j]=h[i-1][j]+1;
			else h[i][j]=0;
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++) ans=max(ans,get(h[i]));//枚举每一行,不断更新 
	cout<<ans*3<<endl;
	return 0;
}

(2)悬线法:https://www.cnblogs.com/Tyouchie/p/11382288.html

代码如下:https://www.acwing.com/solution/content/4873/
 

补充:求直方图最大面积:栈解法:https://www.nowcoder.com/questionTerminal/e3f491c56b7747539b93e5704b6eca40

代码如下:

int get(vector<int> &height){
	int ans=0;
	stack<int> st;
	for(int i=0;i<height.size();i++){
		if(st.empty()||height[i]>st.top()){//大于栈顶元素,入栈 
			st.push(height[i]);
		}
		else{
			int count=0;//记录弹出次数 
			/*如果栈非空 同时高度小于等于栈顶元素,那么就一直弹出栈顶元素,
			边弹出边计算更新ans, 直至height[i]>栈顶元素,此时将弹出的那几个数用height[i]替代入栈,最后height[i]自己再入栈 
			*/
			while(!st.empty()&&height[i]<=st.top()){
				count++;
			    ans=max(ans,st.top()*count);
			    st.pop();
			}
			while(count--){
				st.push(height[i]);//让这个代替前面弹出的几个入栈 
			}
			st.push(height[i]);//自己入栈 
		}
	}
	int count=1;
	while(!st.empty()){//最后形成一个升序的栈 
		ans=max(ans,st.top()*count);//不断比较并更新 
		st.pop();
		count++;
	}
	return ans;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值