山东大学程序设计第五周作业

第一题

最大矩形

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

在这里插入图片描述

Input

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

Output

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

Example
  • input
    7 2 1 4 5 1 3 3
    4 1000 1000 1000 1000
    0
  • output
    8
    4000

解题思路

这个题的思想是不断的维护着一个单调非减栈,算出各个高度对应的最大面积。来个栗子演示程序的运行:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

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

int main()
{
	ios::sync_with_stdio(false);
	int n;
	cin>>n;
	while(n!=0)
	{
		long long int a[n+1]={0};
		stack<long long int> s;
		for(int i=0;i<n;i++) cin>>a[i];
		long long int ans=0,area=0;
		int index=0;
		for(int i=0;i<=n;i++)
		{
			if(!s.empty()) index=s.top();
			while(!s.empty()&&a[i]<a[index])
			{
				index=s.top();
				s.pop();
				if(!s.empty())
					area =a[index]*(i - s.top() - 1);//i代表最右边界的右边一个单位 
				else
					area =a[index]*i;//最低点一定是最后出栈的 
				ans=max(area,ans);
				if(!s.empty())
					index=s.top();
			}
			s.push(i);
		}
		cout<<ans<<endl;
		cin>>n;
	}
}

第二题

长数组与操作

给一个长度为n个数组,执行q此操作。每次操作给出操作的左右区间以及区间中所有元素所加的数,求q此操作后该数组中各个元素值。

Input

第一行有两个数,n和q,n代表n个数,q代表q个操作
第二行有n个数
接下来会有q行,每行有三个数字,前两个数字为被改变区间的左端和右端,第三个数字代表区间内加上的数字

Output

输出改变后的数组

Example
  • input1
    4 2
    -3 6 8 4
    4 4 -2
    3 3 1
  • output1
    -3 6 9 2
  • input2
    2 1
    5 -2
    1 2 4
  • output2
    9 2
  • input3
    1 2
    0
    1 1 -8
    1 1 -6
  • output3
    -14

解题思路

这个题很简单,但是要利用差分数组来求解答案,不然一定会TE。给数组中某一个区间同时加上或者减去同一个数,只需要改变差分数组相应区间的首部的位置和尾部+1的位置即可,操作结束后求出最新的数组即可。

代码实现

#include <iostream>
using namespace std;
int main()
{
	ios::sync_with_stdio(false);
	int n,q;
	cin>>n>>q;
	long long int a[n+1]={0},b[n+1]={0};
	for(int i=1;i<=n;i++) 
	{
		cin>>a[i];
		b[i]=a[i]-a[i-1];
	}
	long long int left,right,c;
	for(int i=0;i<q;i++)
	{
		cin>>left>>right>>c;
		b[left]+=c;
		b[right+1]-=c;
	} 
	for(int i=1;i<=n;i++) 
	{
		b[i]+=b[i-1];
		cout<<b[i]<<" ";
	}
}

第三题

平衡字符串

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

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

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

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

Input

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

Output

一个整数表示答案

Example
  • input1
    QWER
  • output1
    0
  • input2
    QQWE
  • output2
    1
  • input3
    QQQW
  • output3
    2
  • input4
    QQQQ
  • output4
    3

解题思路

这个题的核心思想是尺取法。用num数组分别标记四个字母在str中出现的次数,彼此相等则已经是平衡的,否则记录不在[L,R]区间内的四个字母的个数,然后通过改变区间内的字母使得他们的个数彼此相等。如果此时区间内剩余没有改变的字母个数为4的倍数,说明该区间符合条件,L++,否则R++。

代码实现

#include <iostream>
#include <cstring> 
using namespace std;
char judge[4]={'Q','W','E','R'}; 
char str[100000];
int main()
{
	ios::sync_with_stdio(false);
	cin>>str;
	int len; 
	int num[4]={0};
	for(len=0;str[len]!='\0';len++)
		for(int j=0;j<4;j++)
			if(judge[j]==str[len]) { num[j]++; break; }
	if(num[0]==num[1]&&num[1]==num[2]&&num[2]==num[3]) 
	{
		cout<<0<<endl;
		return 0;
	}
	int l=0,r=0;
	int ans=len;
	for(int i=0;i<4;i++)
		if(str[0]==judge[i]){ num[i]--;break; }
	while(r<len)
	{
		int max=0;
		for(int i=0;i<4;i++)
			if(num[i]>max) max=num[i];
		
		int total=r-l+1,t=0;
		for(int i=0;i<4;i++) t+=(max-num[i]);
		int f=total-t;
		
		if (f>=0 && f % 4 == 0) {
				ans = min(ans, total);
				for(int i=0;i<4;i++) 
					if(str[l]==judge[i]) num[i]++;
				l++;
				
			}
		else {	
				r++;
				for(int i=0;i<4;i++) 
					if(str[r]==judge[i]) num[i]--;

			}
		}
		cout << ans << endl;
	}

第四题

滑动窗口

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

在这里插入图片描述

Input

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

Output

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

Example
  • input
    8 3
    1 3 -1 -3 5 3 6 7
  • output
    -1 -3 -3 -3 3 3
    3 3 5 5 6 7

解题思路

本题的核心思路是不断的维护一个单调队列,队首索引与当前遍历时的位置索引的距离为k时(假设队列不空),就将队首元素出队(这个元素是上几个窗口的最值),然后判断当前位置元素与队尾元素的大小,以求窗口最小值为例,当前位置元素小于队尾元素时,就将队尾元素出队,队空或者当前位置元素大于等于队尾元素时,将当前位置元素入队,紧接着将队首元素输出,依次遍历整个数组两次,分别求出最小值和最大值。

代码实现

#include <iostream>
using namespace std;

int a[1000001],q[2000001];
int main()
{
	ios::sync_with_stdio(false);
	int n,k;
	cin>>n>>k;
	for(int i=0;i<n;i++) cin>>a[i];
	int front=0,back=-1,size=0;
	for(int i=0;i<n;i++)
	{
		if(size && i - q[front] == k){ front++; size--;}
		while (size && a[q[back]] >= a[i])
		{
			back--; size--;
		} 
		q[++back]=i;size++;
		if (i >= k-1) cout << a[q[front]] << " ";
	}
	cout << endl;	
	front=0,back=-1,size=0;
	for (int i = 0; i < n; i++) {
		if(size && i - q[front] == k) { front++;size--; }
		while (size && a[q[back]] < a[i]) 
		{
			back--;size--;
		}
		q[++back]=i;size++;
		if (i >= k-1) cout << a[q[front]] << " ";
	}
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值