week5作业

题目一 最大矩形

题目描述

给一个直方图,求直方图中的最大矩形的面积。例如,下面这个图片中直方图的高度从左到右分别是2, 1, 4, 5, 1, 3, 3, 他们的宽都是1,其中最大的矩形是阴影部分。
在这里插入图片描述

input

输入包含多组数据。每组数据用一个整数n来表示直方图中小矩形的个数,你可以假定1 <= n <= 100000. 然后接下来n个整数h1, …, hn, 满足 0 <= hi <= 1000000000. 这些数字表示直方图中从左到右每个小矩形的高度,每个小矩形的宽度为1。 测试数据以0结尾。

output

对于每组测试数据输出一行一个整数表示答案。

example

input 1

7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0

output 1

8
4000

做法与思路

单调栈例题,事实上如果这个题不事先告知使用单调栈的话,对我来说做起来还是相当困难的。
矩形是由相邻的条形块构成的,所以可以从左向右遍历条形块,找到每个矩形的起点终点和高度。这个问题也可以额外开两个数组解决,不过单调栈的使用可以大大简化操作。
条形块使用结构体表示,包含该块的横坐标(也可理解为序号)和高度。保持栈底是最小元素,向上高度递增,将条形块自左向右入栈。
在栈中存储的条形块对应的坐标就是该矩形的右侧端点,它弹出后栈顶条形块的坐标就是该矩阵的左侧端点。
这样遍历完后可能栈中还剩余条形块,可以考虑在图的两侧加入一个高度为-1的条形块来将栈中所有有意义的条形块弹出。
当然也可以直接把栈按照上述法则清空。
这个题还有一个坑点,面积可能超过了int的上限,为此我迷茫了好久不知道错误在哪…

代码

#include <iostream>
#include <stack>
#include <cmath>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
struct juxing
{
	int i;
	long long value;
	juxing()
	{
		i=0;value=0;
	}
	juxing(int a,long long b)
	{
		i=a;value=b;
	}
};
stack<juxing> t;

long long a;
long long sum;
int main(int argc, char** argv) {
	t.push(juxing(0,-1));
	int n;
	while(cin>>n)
	{
		sum=0;
		if(n==0) return 0;
		for(int i=1;i<=n;i++)
		{
			cin>>a;
			while((t.size()>1)&&(a<=t.top().value))
			{
				juxing tem=t.top();
				t.pop();
				long long sum2=tem.value*(i-t.top().i-1);
				if(sum2>sum)
				{
					sum=sum2;
				}
			}
			t.push(juxing(i,a));
		}
		while(t.size()>1)
		{
			juxing tem=t.top();
			t.pop();
		    long long sum2=tem.value*(n+1-t.top().i-1);
			if(sum2>sum)
			{
				sum=sum2;
			}
		}
		cout<<sum<<endl;
	}
	return 0;
}

2、6、20、21、23、25、31、32、34、35、37、38

题目二 TT的魔力猫

题目描述

Thanks to everyone’s help last week, TT finally got a cute cat. But what TT didn’t expect is that this is a magic cat.

One day, the magic cat decided to investigate TT’s ability by giving a problem to him. That is select n cities from the world map, and a[i] represents the asset value owned by the i-th city.

Then the magic cat will perform several operations. Each turn is to choose the city in the interval [l,r] and increase their asset value by c. And finally, it is required to give the asset value of each city after q operations.

Could you help TT find the answer?

input

第一行有两个数 n, q (1<=n,q<=2*10^5) ,n表示城市的个数,q表示操作次数。
第二行包含n个数,分别代表每个城市的资产ai。(-10^6 <=ai<=10^6)
接下来q行,每一行代表一个操作, l ,r ,c
(1≤l≤r≤n,−10^5 ≤c≤ 10^5)(1≤ l ≤ r ≤n,−10^5 ≤ c ≤ 10^5)

output

每个城市最后的资产

example

在这里插入图片描述

做法与思路

l和n的数量级过大,如果遍历增加的话会超时,所以要使用差分的方法解决。
假设原数组是a[i],那么我们开一个差分数组b,使b[0]=a[0],b[i]=a[i]-a[i-1].那么a[i]=b[0]+b[1]+…+b[i].
这样我们在对[l,r]区间进行操作的时候只需要操作两侧的端点对应的b[i]就可以了。
总体来说比较简单。

代码

#include <iostream>

/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
long long a[300010];
long long b[300010];
long long sum;
int main(int argc, char** argv) {
	int n,numble;
	cin>>n>>numble;
	for(int i=0;i<n;i++)	cin>>a[i];
	b[0]=a[0];
	for(int i=1;i<n;i++)	b[i]=a[i]-a[i-1];
	int x,y,z;
	for(int i=0;i<numble;i++)
	{
		cin>>x>>y>>z;
		b[x-1]+=z;
		b[y]-=z;
	}
	for(int i=0;i<n;i++)
	{
		sum+=b[i];
		cout<<sum<<" ";
	}
	return 0;
}

2、6、20、21、23、25、31、32、34、35、37、38

题目三 平衡字符串

题目描述

一个长度为 n 的字符串 s,其中仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。

如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。

现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?

如果 s 已经平衡则输出0。

input

一行字符表示给定的字符串s

output

一个整数表示答案

example

input 1

QWER

output 1

0

input 2

QQQW

output 2

2

做法与思路

由于题目所要的是连续区间,所以可以使用尺取法解决。
尺取法预设左端点和右端点都在0点处,如果此时不满足条件,就让右端点+1,如果满足条件,就让左端点-1,直到遍历完所有点(或者说右端点已经碰到最右侧同时又发生了不满足条件的情况)。
这就是解决问题的大致模板,剩下的问题就是判断是否“满足条件”。不妨用l表示左端点,r表示右端点。
我们的目标是达成四种字母的数量相同,即让他们的个数都是n/4。而框内的字母可以替换,所以我们只需要统计框外的四种字母各自的数量,如果框外字母有数量大于n/4的,那么无论框内如何替换都无法使其减小到n/4,此时不满足条件。如果框外的字母都是小于等于n/4的,那么显然可以通过调换框内的元素达成n/4,即满足条件。

代码

#include <iostream>
#include <map>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
map<char,int> s;
int range=9999999;

int main(int argc, char** argv) {
	s['Q']=0;
	s['W']=0;
	s['E']=0;
	s['R']=0;
	string t;
	cin>>t;
	for(int i=0;i<t.size();i++)
	{
		s[t[i]]++;//统计各个字母的占比 
	}
	int n=t.size()/4;
	int l=0,r=-1;
	while((r<4*n)&&(l>=0)&&(range>0))
	{
		while((s['Q']>n)||(s['W']>n)||(s['E']>n)||(s['R']>n))
		{
			if(r<4*n)
			{
				r++;
				s[t[r]]--;
			}
			else break;
		}
		while((s['Q']<=n)&&(s['W']<=n)&&(s['E']<=n)&&(s['R']<=n))
		{		
			if(r-l+1<range)	range=r-l+1; 
			if(l<=r)
			{
				s[t[l]]++;
				l++;
			}
			else break;
		}
	//	cout<<range<<endl;
	}
	cout<<range;
	return 0;
}

题目四 滑动窗口

题目描述

ZJM 有一个长度为 n 的数列和一个大小为 k 的窗口, 窗口可以在数列上来回移动. 现在 ZJM 想知道在窗口从左往右滑的时候,每次窗口内数的最大值和最小值分别是多少. 例如:
数列是 [1 3 -1 -3 5 3 6 7], 其中 k 等于 3.
Window position Minimum value Maximum value
[1 3 -1] -3 5 3 6 7 -1 3
1 [3 -1 -3] 5 3 6 7 -3 3
1 3 [-1 -3 5] 3 6 7 -3 5
1 3 -1 [-3 5 3] 6 7 -3 5
1 3 -1 -3 [5 3 6] 7 3 6
1 3 -1 -3 5 [3 6 7] 3 7

input

输入有两行。第一行两个整数n和k分别表示数列的长度和滑动窗口的大小,1<=k<=n<=1000000。第二行有n个整数表示ZJM的数列。

output

输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。

example

input 1

8 3
1 3 -1 -3 5 3 6 7

output 1

-1 -3 -3 -3 3 3
3 3 5 5 6 7

做法与思路

单调队列的例题,因为已经事先知道了使用单调队列,所以解决起来比较简单,分别维护两个单调队列,一个递增一个递减,将每一次移位后选出的最大值和最小值额外存储到数组中即可。
同时要注意如果队列中的元素已经在框外,则也需要将其取出,这里我使用了结构体来表示,结构体中包含了数字的大小和数字的序号,如果当前数字的序号和队列中数字序号的差值大于框的长度,那么需要出队列。
值得一提的是这个题对于时间的要求似乎相当严格,我最开始是使用的deque来解决问题,结果怎么跑都是tle,后来甚至把输入用scanf还是tle,最后自己手动维护数组才解决的问题。

代码

#include <iostream>
#include <deque> 
#include <cstdio>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int s[1000100]; 
int maxx[1001000];
int minn[1001000];
struct shu
{
	int i;
	int value;
	shu(){i=0;value=0;}
	shu(int a,int b){i=a;value=b;} 
};
deque<shu> xiao;
deque<shu> da;
int main(int argc, char** argv) {
	int a,b;
	scanf("%d%d",&a,&b);
	for(int i=0;i<a;i++)	scanf("%d",&s[i]);
	int maxp=0,minp=0;

	for(int i=0;i<b-1;i++)
	{
		while((!da.empty())&&(s[i]>da.back().value))
		{
			da.pop_back();
		}
		while((!xiao.empty())&&(s[i]<xiao.back().value))
		{
			xiao.pop_back();
		}
		da.push_back(shu(i,s[i]));
		xiao.push_back(shu(i,s[i]));
	}
	for(int i=b-1;i<a;i++)
	{
		while((!da.empty())&&(s[i]>da.back().value))
		{
			da.pop_back();
		}
		da.push_back(shu(i,s[i]));		
		while(i-da.front().i>=b)	da.pop_front();
		maxx[maxp]=da.front().value;
		maxp++;
		
	
		while((!xiao.empty())&&(s[i]<xiao.back().value))
		{
			xiao.pop_back();
		}
		xiao.push_back(shu(i,s[i]));	
		while(i-xiao.front().i>=b)	xiao.pop_front();
		minn[minp]=xiao.front().value;
		minp++;
	}

	for(int i=0;i<maxp-1;i++)	printf("%d ",minn[i]);
	printf("%d\n",minn[maxp-1]); 
	for(int i=0;i<minp-1;i++)	printf("%d ",maxx[i]);
	printf("%d\n",maxx[minp-1]) ;

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值