WEEK 5作业

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

思路:
原题来自csp2013年,但本题的数据范围更大,不适用于o(n^2)的算法,于是用到单调栈的算法,对于单调栈算法来说,相当于从第一个点开始,将其与之前的比较,然后用st[r]来存储这个点能延伸到的最右边的位置,但会有一些数没有得到最右边的位置,即R[i],因为会被他后面的从后往前遍历的一个大于它的数阻挡,故之前的那些数都没有R[i]值,或者说是初始值0,所以他们的值可以贯穿整个右边,R[i]即为n。
然后对于其左端点,可以将原数组逆序,然后相同的方法,得其左端点,这样得到的下标并不是原来的下标,故需转化一下。
最后,遍历所有点,底乘高取最大即可。

代码:

#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
long long a[100001];
long long b[100001];
int n;
long long st[100001];
long long R[100001];
long long L[100001];
long long ans=-1;
long long MAX(long long a,long long b)
{
	return a>=b?a:b;
}
void solve1(){
	memset(st,0,sizeof(st));
	int l = 1, r = 0; // r-l+1 = 0
	for(int i = 1; i <= n; i++){
		while(l <= r && a[st[r]] > a[i]){
			R[st[r]] = i-1;
			r--;
		}
		st[++r] = i;
	}
	for(int i=1;i<=n;i++)
	{
		if(R[i]==0)
		R[i]=n;
	}
}
void solve2()
{
	memset(st,0,sizeof(st));
	int l = 1, r = 0; // r-l+1 = 0
	for(int i = 1; i <= n; i++){
		while(l <= r && b[st[r]] > b[i]){
			L[st[r]] = i-1;
			r--;
		}
		st[++r] = i;
	}
	for(int i=1;i<=n;i++)
	{
		if(L[i]==0)
		L[i]=n;
	}
}
void solve3()
{
	ans=-1;
	for(int i=1;i<=n;i++)
	{
		ans=MAX(ans,(R[i]+L[n+1-i]-n)*a[i]);
	}
	cout << ans << endl;
}
int main()
{
	while(scanf("%d",&n))
	{
		if(n==0)
		break;
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	memset(L,0,sizeof(L));
	memset(R,0,sizeof(R));
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		b[n+1-i]=a[i];
	}	
	solve1();
	solve2();
	solve3();
}}

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 nn cities from the world map, and a[i]a[i] represents the asset value owned by the ii-th city.

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

Could you help TT find the answer?

Input
The first line contains two integers n,qn,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 aa: integer numbers a1,a2,…,ana1,a2,…,an (−106≤ai≤106)(−106≤ai≤106).

Then qq lines follow, each line represents an operation. The ii-th line contains three integers l,rl,r and cc (1≤l≤r≤n,−105≤c≤105)(1≤l≤r≤n,−105≤c≤105) for the ii-th operation.

Output
Print nn integers a1,a2,…,ana1,a2,…,an one per line, and aiai should be equal to the final asset value of the ii-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

思路:
用差分的话可以减少计算量,考虑用另一个数组b存储数组a的相邻两项差,首项相同,故当a中一段[l,r]变化时,b中只需变化两个数b[l],b[r+1],若r为a的右端点,则b只需改变一个数b[l],在此基础上我们便可以进行一系列的变化,最终再化为数组a进行输出即可。

代码:

#include<iostream>
using namespace std;
long long a[200005];
long long b[200005];
int n,l,r,q,c;
void solve()
{
	if(r==n)
	{
		b[l]+=c;
	}
	else
	{
		b[l]+=c;
		b[r+1]-=c;
	}
}
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		if(i==1)
		b[i]=a[i];
		else
		b[i]=a[i]-a[i-1];
	}
	for(int i=1;i<=q;i++)
	{
		scanf("%d%d%d",&l,&r,&c);
		solve();
	}
	for(int i=1;i<=n;i++)
	{
		if(i==1)
		{a[1]=b[1];cout << b[i];}
		else
		{a[i]=a[i-1]+b[i];cout << " " << a[i];
		}
	}
}

C - 平衡字符串
题目:
一个长度为 n 的字符串 s,其中仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。

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

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

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

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

Output
一个整数表示答案

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’.

思路:
要判断取哪一段使得字符平衡,则依次对每一段进行遍历,当一段满足要求时,则l++;不满足要求时,则r++;同时利用差分将sum值进行加或减,而对于判断这一段可不可以平衡字符,需要求出最大的字符max减去四种sum 的和,即判断自己这样的长度,能不能将外面的填坑,如果total<0,则填不了,如果total%4!=0,则其填完后,自己不能保证剩下的没填的组成QWER,故同样不满足题意。
最后取最小的ans即可。

代码:

#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
char s[100005];
int total,sum1=0,sum2=0,sum3=0,sum4=0,ans=100005,n;
bool solve(int l,int r)
{
	total=r-l+1;
	int maxx=max(max(sum1,sum2),max(sum3,sum4));
	total-=4*maxx-n+r-l+1;
	if(total>=0&&total%4==0) return true;
	else return false;
}
void solve1()
{
	int l=0,r=-1;
	while(r<n)
	{
		//sum1=sum2=sum3=sum4=0;
		if(solve(l,r))
		{
			ans=min(ans,r-l+1);l++;
			if(ans==0)
			break;
			switch(s[l-1]){
						case 'Q':sum1++;break;
						case 'W':sum2++;break;
						case 'E':sum3++;break;
						case 'R':sum4++;break;
						}
			
		}
		else
		{
			r++;
			switch(s[r]){
						case 'Q':sum1--;break;
						case 'W':sum2--;break;
						case 'E':sum3--;break;
						case 'R':sum4--;break;
						default :break;
						}
		}
		
	}
	cout << ans << endl;
}
int main()
{
	scanf("%s",&s);
	n=strlen(s);
	for(int i=0;i<n;i++)
		{
			switch(s[i]){
						case 'Q':sum1++;break;
						case 'W':sum2++;break;
						case 'E':sum3++;break;
						case 'R':sum4++;break;
						}
		}
	//cout << n << endl;
	solve1();
}

D - 滑动窗口
题目:
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
输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。
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

思路:
此题用到的单调队列,我们利用r来代表队尾的指针,l来代表队首的指针,故q[l]即为队首的下标,q[r]为队尾的下标,如果下一个数比前一个数小的话,一直向前遍历到比它大的,然后插入队中,因为往前遍历时比他大的数都会踢出队列,故栈顶下标即为q[r++],因为这个数在队列中最大。
然后将队尾与队首下标进行判断,若大于等于k,则l对应的下标已经对后面的取最小没有影响,故踢掉队首,然后就是每次循环都输出最小值即可。

求最大数则创建一个单调递减大的队列即可,过程类似。

代码:

#include<iostream>
using namespace std;
int a[1000001];
int q[1000001];
int n,k;
void solve()
{
	int l=1,r=0;
	for(int i=1;i<=n;i++)
	{
		while(l<=r&&a[q[r]]>a[i])
		{
			r--;//队尾指针变化 
		}
		q[++r]=i;
		if(q[r]-q[l]>=k) l++;
		if(i>=k)
		cout << a[q[l]];
		if(i!=n&&i>=k)
		cout << " ";
	}
	
}
void solve1()
{
	int l=1,r=0;
	for(int i=1;i<=n;i++)
	{
		while(l<=r&&a[q[r]]<a[i])
		{
			r--;
		}
		q[++r]=i;
		if(q[r]-q[l]>=k) l++;
		if(i>=k)
		cout << a[q[l]];
		if(i!=n&&i>=k)
		cout << " ";
	}
}
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	solve();
	cout << endl;
	solve1();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值