第5周作业

A - 最大矩形

问题描述

给一个直方图,求直方图中的最大矩形的面积。例如,下面这个图片中直方图的高度从左到右分别是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

解题思路

为了计算出最大矩形的面积,我们要计算出各个高度对应的最大宽度,然后依此计算出各高度对应的最大矩形面积,从而计算出整个图形对应的最大矩形面积。为了计算出各高度对应的最大宽度,我们可以使用单调递增栈来解决,但栈中元素并不是具体的高度,而是其对应的下标。因此每当有元素出栈时,就可以计算出栈顶元素所对应高度的最大矩形面积;之后只需要计算出,各最大面积的最大值即可。

注意:由于0 <= hi <= 1000000000,所以数据类型不能选用int, 应该选用long long int;此外为了确保各高度都可出栈,要向元素底插入一个0,从而确保各元素都能出栈。

代码

#include<iostream>
#include<stack>
#include<stdio.h> 
#include<cmath>
using namespace std;

int main()
{
	int n = 1;
	while(true)
	{
		cin>>n;
		if(n==0)
			return 0;
		long long int a[n+1];
		for(int i=0;i<n;i++)
			scanf("%lld", &a[i]);
		stack<int> p;
		a[n]=0;             //确保最后会弹出栈,计算出最大面积 
		long long int s = 0, r = 0;
		int m = 0;
		for(int i=0;i<=n;i++)
		{
			if(!p.empty())
			 	m = p.top();
			while(!p.empty()&&a[i]<a[m])
			{
				m = p.top();
				p.pop();
				if(!p.empty())
					r =a[m]*(i - p.top() - 1);	
				else
					r =a[m]*i;
				s = max(r,s);
				if(!p.empty())
					m = p.top() ;
			}
			p.push(i);
		}
		printf("%lld\n",s);
	}
} 

B - TT’s Magic Cat

问题描述

长度为 n 的数组,一共 q 次操作, 每次操作给出 L, R , c,表示区间 [L, R] 中各个数均加 上 c ,求 q 次操作结束后,数组中各个元素值。

Input

第一行包含两个整数n,q(1≤n,q≤2*100000)
第二行包含序列a的元素:整数a1,a2,…,an(-1000000≤a[i]≤1000000)。
然后有q行,每行代表一个运算。第i行包含第i个运算的三个整数l,r和c(1 ≤ l ≤r ≤n,−100000≤c≤100000)。

Output

每行打印n个整数a1,a2,…,an,并且ai应该等于应该等于a数组的最终元素。

Example

Input1

4 2
-3 6 8 4
4 4 -2
3 3 1

Output1

-3 6 9 2

Input2

1 2
0
1 1 -8
1 1 -6

Output2

-14

解题思路

将原数组转变为差分数组,将区间修改转变为单点修改,即a[L, R] += c ⇔ b[L] += c, b[R+1] -= c ,B 数组前缀和即为 A 数组最终数值,可以通过a[i] = a[i-1]+b[i]计算出数组a的所有元素并输出。

注意:同A题,这一题也要注意数据类型的选择。

代码

#include <iostream>
#include <stdio.h>
using namespace std;

long long int a[300000];
long long int b[300000];

int main()
{
	int m, n;
	cin>>n>>m;
	a[0] = 0;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld", &a[i]);
		b[i] = a[i] - a[i-1];
	}
	while(m!=0)
	{
		int l, r , count;
		scanf("%d%d%d", &l, &r, &count);
		b[l] = b[l]+count;
		b[r+1] = b[r+1]-count;
		m--;
	}
	for(int i=1;i<=n;i++)
	{
		a[i] = b[i]+a[i-1];
		printf("%lld ",a[i]);
	}
	printf("\n");			
}

C - 平衡字符串

问题描述

一个长度为 n 的字符串 s,其中仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。
如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。
现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?
如果 s 已经平衡则输出0。

Input

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

Output

一个整数表示答案

Example

Input1

QWER

Output1

0

Input2

QQQQ

Output2

3

Input3

QQWE

Output3

1

解题思路

本题可采用尺取法进行操作。给定 [L, R],判断是否满足要求, 用 sum1, sum2, sum3, sum4 分别记录不包含区间 [L, R] 这一段时,字符 ‘A’, ‘B’, ‘C’, ‘D’ 的个数, 先通过替换使 4 类字符数量一致,再判断剩余空闲位置是否 为 4 的倍数 。

MAX = max(sum1, sum2, sum3, sum4)
TOTAL = R – L + 1 
FREE = TOTAL -[(MAX-sum1)+(MAX-sum2)+(MAX-sum3)+(MAX-sum4)]

若 FREE ≥ 0 且为 4 的倍数,则满足要求;否则不满足。
若当前 [L, R] 满足要求,则 L++,但要保证R >= L;若当前 [L, R] 不满足要求,则 R++。
然后我们只需要从前往后边遍历整个数组,得出最小的TOTAL即可。

代码

#include<iostream>
#include<string>
#include<string.h>
#include<stdio.h>
#include<cmath>
using namespace std;

int a, b, c, d;
string s;

bool judge(int L, int R, int zero)
{
	int a1=0, b1=0, c1=0, d1=0;
	for(int i=L;i<=R&&zero!=0;i++)
	{
		if(s[i]=='Q')
			a1++;
		else if(s[i]=='W')
			b1++;
		else if(s[i]=='E')
			c1++;
		else if(s[i]=='R')
			d1++;
	}
	a1 = a-a1, b1 = b-b1, c1=c-c1, d1=d-d1;
	int max1 = max(a1, b1);
	max1 = max(max1, c1);
	max1 = max(max1, d1);
	int sum =max1*4-a1-b1-c1-d1;
	int total = R-L+1;
	int free = total - sum;
	if(free<0||free%4!=0)
		return false;
	else
		return true;
}
int main()
{
	cin>>s;
	a = 0, b = 0, c = 0, d = 0;
	for(int i=0;i<s.length();i++)
	{
		if(s[i]=='Q')
			a++;
		else if(s[i]=='W')
			b++;
		else if(s[i]=='E')
			c++;
		else if(s[i]=='R')
			d++;
	}
	int L=0, R=0, min1 = s.length();
	if(judge(0, 0, 0))
	{
		cout<<"0"<<endl;
		return 0;
	}
	while(true)
	{
		if(!judge(L, R, 1)&&R<s.length()-1)
			R++;
		else if(judge(L, R, 1)&&L==R)
		{
			min1 = min(min1, R-L+1);
			L++, R++;
		}	
		else if(judge(L, R, 1)&&L<R)
		{
			min1 = min(min1, R-L+1);
			L++;
		}		
		if(!judge(L, R, 1)&&R==s.length()-1)	
		{
			cout<<min1<<endl;
			return 0;
		}
	}
} 

D - 滑动窗口

问题描述

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

Input

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

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

解题思路

可以使用单调递增或递减的双向队列来计算出每一个窗口的最大值和最小值,我们要做的就是在元素入队时维持队列的单调性,并把不属于当前窗口的元素弹出队列。然后当前窗口的最大或最小元素就是队首元素,分别用两个数组进行存储即可。除了用双向队列deque实现外,也可以用数组实现,但具体流程都是一样的,但数组的运行时间更短。

代码1(数组实现)

#include <iostream>
using namespace std;

int p[1000001], q[1000001], a[1000001], b[1000001], c[1000001];

int main()
{
	int m, n, count;
	cin>>m>>n;
	for(int i=0;i<m;i++)
		b[i]=c[i]=0;
	for(int i=0;i<m;i++)
		scanf("%d",&a[i]);
	int front = 1, back = 0, max = 0, min = 0;
	for(int i=0;i<m;i++)
	{
		if(front<=back)
		{
			count = p[back];
			max = a[count];
			while(a[i]<max&&front<=back)
			{
				back--;
				if(front<=back)
				{
					count = p[back];
					max = a[count];
				}		
			}
		}	
		p[++back]=i;
		while(p[front]<=i-n)
			front++;
		if(i>=n-1)
		{
			int j = i-n+1;
			count = p[front];
			b[j] = a[count];
		}	
	}
	front = 1, back =0;
	for(int i=0;i<m;i++)
	{
		if(front<=back)
		{
			count = q[back];
			min = a[count];
			while(a[i]>min&&front<=back)
			{
				back--;
				if(front<=back)
				{
					count = q[back];
					min = a[count];
				}		
			}
		}	
		q[++back]=i;
		while(q[front]<=i-n)
			front++;
		if(i>=n-1)
		{
			int j = i-n+1;
			count = q[front];
			c[j] = a[count];
		}	
	}
	for(int i=0;i<m-n+1;i++)
		printf("%d ", b[i]); 
	printf("\n");
	for(int i=0;i<m-n+1;i++)
		printf("%d ", c[i]);
	printf("\n");
	return 0;
}

代码2(deque实现)

#include <iostream>
#include <stdio.h> 
#include <deque>

int main()
{
	int m, n, back;
	scanf("%d %d", &m, &n);
	int *a, *b, *c;
	a = new int[m], b = new int[m], c = new int[m]; 
	for(int i=0;i<m;i++)
		b[i]=c[i]=0;
	for(int i=0;i<m;i++)
		scanf("%d",&a[i]);
	std::deque<int> p,q;
	for(int i=0;i<m;i++)
	{
		if(!p.empty())
		{
			back = a[p.back()];
			while(a[i]<back)
			{
				p.pop_back();
				if(p.empty())
					break;
				back = a[p.back()];
			}
		}	
		p.push_back(i);
		while(p.front()<=i-n)
			p.pop_front();
		if(i>=n-1)
		{
			int j = i-n+1;
			b[j] = a[p.front()];
		}	
	}
	for(int i=0;i<m;i++)
	{
		if(!q.empty())
		{
			back= a[q.back()];
			while(a[i]>back)
			{
				q.pop_back();
				if(q.empty())
					break;
				back = a[q.back()];
			}
		}	
		q.push_back(i);
		while(q.front()<=i-n)
			q.pop_front();
		if(i>=n-1)
		{
			int j = i-n+1;
			c[j] = a[q.front()];
		}	
	}
	for(int i=0;i<m-n+1;i++)
		printf("%d ", b[i]); 
	printf("\n");
	for(int i=0;i<m-n+1;i++)
		printf("%d ", c[i]);
	printf("\n");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值