week5作业总结

A:最大矩形:
给一个直方图,求直方图中的最大矩形的面积。例如,下面这个图片中直方图的高度从左到右分别是2, 1, 4, 5, 1, 3, 3, 他们的宽都是1,其中最大的矩形是阴影部分。
在这里插入图片描述
Input:
输入包含多组数据。每组数据用一个整数n来表示直方图中小矩形的个数,你可以假定1 <= n <= 100000. 然后接下来n个整数h1, …, hn, 满足 0 <= hi <= 1000000000. 这些数字表示直方图中从左到右每个小矩形的高度,每个小矩形的宽度为1。 测试数据以0结尾。
Output:
对于每组测试数据输出一行一个整数表示答案。
Sample Input:
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
Sample Output:
8
4000
题目总结:这个题目是对于单调栈的一个应用,单调栈在处理这种问题中有着一定的优越性。我个人认为难度在于想到单调栈这种方法,也即如何将题目的解法归为一种有效算法,其中算法具有较高的统一性,没有过多的条件语句。拿这道题目举例来说,自己首先想到的是尺取,这是潜意识的,但这是不可行的,因为不系统,不具有统一性。其实,对于这道题来讲,给我最大的感受就是与计算器的设置由前序表达式转为后序表达式中的思想有些雷同,准确地讲,栈本身就具有解决这种问题的能力。
我们可以将其抽象为一个单调递增的函数,当其存在拐点时,其拐点处的元素应当被弹出,但是,这道题我们维护的单调栈不具有严格的单调性,因此,我们将其左右两边的扩展程度分别进行求解。另外这道题中的栈我们是利用的数组进行模拟的,在这里,我们同样也要认识到数组的强大作用,尤其是元素下标与元素值的搭配使用,存在一定的灵活性。另一方面,我们要注意数据的范围,这道题目int会溢出,应当养成做题前判断数据的范围的习惯。
代码部分:

#include<iostream>
#include<stdio.h>
#include<cmath>
using namespace std;
int a[100000]={0};
int r[100000]={0};
int l[100000]={0};
int stack[100000]={0};
long long ans=0;
void funcright(int* a,int n)
{
	int left=1,right=0;
	for(int i=0;i<n;i++)
	{
		while(left<=right&&a[i]<a[stack[right]])
		{
			r[stack[right]]=i-1;
			right--;
		}
		stack[++right]=i;
	}
	while(right!=0)
	{
		r[stack[right--]]=n-1;
	}
}
void funcleft(int *a,int n)
{
	int right=1;int left=0;
	for(int i=n-1;i>-1;i--)
	{
		while(right<=left&&a[i]<a[stack[left]])
		{
			l[stack[left]]=i+1;
			left--;
		}
		stack[++left]=i;
	}
	while(left!=0)
	l[stack[left--]]=0;
}
int main()
{
	int n;
	scanf("%d",&n);
	while(n!=0)
	{
		ans=0;
		for(int i=0;i<n;i++)
		{
			scanf("%d",&a[i]);
		}
		funcright(a,n);
		funcleft(a,n);
		for(int i=0;i<n;i++)
		{
			long long ans2=(long long)a[i]*(r[i]-l[i]+1);
			ans=max(ans,ans2);
		}
		cout<<ans<<endl;
		scanf("%d",&n);
	}
}在这里插入代码片

B - TT’s Magic Cat:
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?InputThe first line contains two integers n,q(1≤n,q≤2⋅105)(1≤n,q≤2·105)
— the number of cities and operations.The second line contains elements of the sequence a: integer numbers a1,a2,…,an(−106≤ai≤106).Then q lines follow, each line represents an operation. The i-th line contains three integers l,r and c(1≤l≤r≤n,−105≤c≤105)(1≤l≤r≤n,−105≤c≤105),for the i-th operation.OutputPrint n integers a1,a2,…,an. one per line, and ai should be equal to the final asset value of the i-th city.
Examples Input:
4 2
-3 6 8 4
4 4 -2
3 3 1
Output:
-3 6 9 2
Input:
2 1
5 -2
1 2 4
Output:
9 2
Input:
1 2
0
1 1 -8
1 1 -6
Output:
-14
题目总结:这道题题目的解法是利用了差分的思想,运用了这种思想题目不是很难,但是我们值得学习的是这种思维方式,这种转化的思想,在数组中,变化数据的下标的连续性,使得我们只需利用差分数组控制其两端的变化即可。另外也要注意本题目数据的范围。
代码:

#include<iostream>
#include<stdio.h>
using namespace std;
int n,q;
long long a[1000000]={0};
long long b[1000000]={0}; 
int main()
{ 
    int l=0,r=0,value=0; 
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
	} 
	for(int i=1;i<=n;i++)
	{ 
	    b[i]=a[i]-a[i-1]; 
	}   
	for(int i=0;i<q;i++)
	{
	    scanf("%d%d%d",&l,&r,&value);
	    b[l]=b[l]+value;
	    b[r+1]=b[r+1]-value; 
	}
	for(int i=1;i<n;i++)
	{
		a[i]=a[i-1]+b[i];
		cout<<a[i]<<" ";
	}
	cout<<a[n-1]+b[n]<<endl;
}在这里插入代码片

C - 平衡字符串:
一个长度为 n 的字符串 s,其中仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。
如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。
现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?
如果 s 已经平衡则输出0。Input一行字符表示给定的字符串sOutput一个整数表示答案Examples
Input:
QWER
Output:
0
Input:
QQWE
Output:
1
Input:
QQQW
Output:
2
Input:
QQQQ
Output:
3
Note:1<=n<=10^5,n是4的倍数,字符串中仅包含字符 ‘Q’, ‘W’, ‘E’ 和 ‘R’.

题目总结:
这道题目的解法比较有新意,感觉自己很难想到,因为要平衡字符串,普通思维下,自己喜欢直接去进行考虑使得原串的两端分别向中间进行逐步逼近,但理论证明。这种是不可行的。在大串与其字串之间存在这样的关系:大串可以,子串的情况未知;
大串不可以,子串肯定不可以;子串不可以,大串的情况未知;子串可以,大串一定可以;故我们要充分利用这个隐含条件,对于一个串判断其是否可以时,我们采用将字母统一补齐,然后判断是否为4的倍数即可。另外,对于字串的扩展,当判断到一个字串可以时,我们要对字串进行缩减,接下来判断是否还可以,注意我们始终维护着字串的两端,故这就像是尺取的思想。准确的来讲,我们在以右端点为标准,判断在每一个右端点下,能够获得的满足题意的最小字串的长度。
代码:

#include<iostream>
#include<string>
#include<cmath>
using namespace std;
int sumq=0,sumw=0,sume=0,sumr=0;
int maxx=0;
int ans=100000;
int l=0,r=0;
int total=0;
int main() {
	string s;
	cin>>s;
	for(int i=0; i<s.size(); i++) {//进行初始化; 
		if(s[i]=='Q')
			sumq++;
		if(s[i]=='W')
			sumw++;
		if(s[i]=='E')
			sume++;
		if(s[i]=='R')
			sumr++;
	}
	if(sumq==sumw&&sumw==sume&&sume==sumr)
	{
		cout<<0<<endl;
		return 0;
	}
	while(r<s.size()&&r>=l) {
		if(s[r]=='Q')
			sumq--;
		if(s[r]=='W')
			sumw--;
		if(s[r]=='E')
			sume--;
		if(s[r]=='R')
			sumr--;
		maxx=max(max(sumq,sumw),max(sume,sumr));
		total=r-l+1-(maxx-sumq)-(maxx-sumw)-(maxx-sume)-(maxx-sumr);
		while(total>=0&&total%4==0) {
			ans=min(ans,r-l+1);
			if(s[l]=='Q')
				sumq++;
			if(s[l]=='W')
				sumw++;
			if(s[l]=='E')
				sume++;
			if(s[l]=='R')
				sumr++;
			l++;
			maxx=max(max(sumq,sumw),max(sume,sumr));
		    total=r-l+1-(maxx-sumq)-(maxx-sumw)-(maxx-sume)-(maxx-sumr);
		}
		r++; 
	}
	cout<<ans<<endl;
}在这里插入代码片

D - 滑动窗口滑动窗口:
ZJM 有一个长度为 n 的数列和一个大小为 k 的窗口, 窗口可以在数列上来回移动. 现在 ZJM 想知道在窗口从左往右滑的时候,每次窗口内数的最大值和最小值分别是多少.
例如:数列是[1 3 -1 -3 5 3 6 7], 其中k 等于 3.
在这里插入图片描述
Input:
长度和滑动窗口的大小,1<=k<=n<=1000000。第二行有n个整数表示ZJM的数列。
Output:
输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。
Sample Input:
8 3
1 3 -1 -3 5 3 6 7
Sample Output:
-1 -3 -3 -3 3 3
3 3 5 5 6 7
题目总结:
这道题目的解法是利用单调队列,在这里,我们可以比较一下单调栈与单调队列的不同点,对于单调栈来讲,其维护的知识一种单调性,而对于单调队列来讲,其维护的是单调性具有定区间的性质,也即是局部的单调性;在这里,我们始终以区间右端点为参考系,再求最小值时,我们保留一个单调递增的双向队列,在求最大值时,我们保留一个单调递减的双向队列,这种设定的目的在于保证弹出的点对后来加入的点没有参考价值;对于单调栈与单调队列来讲,认知比较简单,但是从题目论述中意识到应当利用单调栈或是单调队列来解体是比较困难的。在这里,我认为二者的用途都集中体现在寻找“连续数组”中的“优先级”最高的元素方面,只不过单调队列有额外的区间限制,在我们大多数题目中,该“优先级”体现为大小比较。
代码:

#include<iostream>
#include<stdio.h>
using namespace std;
int queue[1000000]={0};
int a[1000000]={0};
int b[1000000]={0};
int ans=0;
int l=1,r=0;
int n,k;
int main()
{
	cin>>n>>k;
	for(int i=0;i<n;i++)
	{
		scanf("%d",&a[i]);
	}
	for(int i=0;i<n;i++)
	{
		while(l<=r&&a[i]<a[queue[r]])
			r--;
		queue[++r]=i;
		if(queue[r]-queue[l]+1>k)
		{
			l++;
		}
		b[queue[r]]=a[queue[l]]; 
	}
	for(int i=k-1;i<n-1;i++)
	{
		cout<<b[i]<<" ";
	}
	cout<<b[n-1]<<endl;
	l=1;
	r=0;
	for(int i=0;i<n;i++)
	{
		while(l<=r&&a[i]>a[queue[r]])
		r--;
		queue[++r]=i;
		if(queue[r]-queue[l]+1>k)
		{
			l++;
		}
		b[queue[r]]=a[queue[l]]; 
	}
	for(int i=k-1;i<n-1;i++)
	{
		cout<<b[i]<<" ";
	}
	cout<<b[n-1]<<endl;
}在这里插入代码片
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值