week5-作业

A - 最大矩形

题意:

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

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


思路:

如果确定了矩形的高度,那么左端点一定越靠左,也就是往左数第一个小于此高度的点,而右端点可以确定往右数第一个小于此高度的点,这就可以利用单调栈分别遍历每个点为高时左右两端的端点采用数组保存,对每个点一次进行遍历,然后循环判断当前栈顶所在点的高是否大于等于当前点的高,如果是则删除栈顶,循环结束后判断栈是否为空,若为空,则证明左端点在最左边。否则就为当前栈顶。接着将该点压入栈中。右断点也是这样操作,只是从尾到头开始。
注意:因为面积的数据大小范围超过int 可以用long long转换


代码:

#include<iostream>
#include<stack>
#include<cmath>
#include<algorithm>
using namespace std;
stack<int> s;
int a[100010], l[100010], r[100010];
int main()
{
	int n;
	
	while (~scanf("%d",&n))
	{
		long long int area=0;
		if (n == 0) exit(0);
		for (int i = 1; i <= n; i++)
		{
			scanf("%d", &a[i]);
		}
		while (!s.empty())
			s.pop();
		for (int i = 1; i <= n; i++)
		{
			while (!s.empty() && a[s.top()] >= a[i])
				s.pop();
			if (s.empty()) 
				l[i] = 0;
			else 
				l[i] = s.top();
			s.push(i);
		}
		while (!s.empty())
			s.pop();
		for (int i = n; i >=1; i--)
		{
			while (!s.empty() && a[s.top()] >= a[i])
				s.pop();
			if (s.empty()) 
				r[i] = n+1;
			else 
				r[i] = s.top();
			s.push(i);
		}
		for (int i = 1; i <= n; i++)
			area = max(area, (long long int)a[i] * (r[i] - l[i]-1));
		cout << area << endl;
	}
	return 0;
}

B - TT’s Magic Cat

题意:

多亏了上周大家的帮助,它终于得到了一只可爱的猫。但没想到的是,这是一只神奇的猫。
有一天,神奇的猫决定调查TT的能力,给他一个问题。即从世界地图中选择n个城市,a[i]表示第i个城市拥有的资产价值。
然后,这只神奇的猫将执行几项操作。每轮选择[l,r]区间内的城市,并将其资产价值增加c。最后,需要给出q操作后各城市的资产价值。
你能帮我找到答案吗?
input:
第一行包含两个整数n q
经营城市和数量。
第二行包含序列a的元素:整数a1,a2,…,an
接下来是q行,每一行代表一个操作。第i行包含三个整数l、r和c,用于第i个操作。
output:
打印n个整数a1,a2,…,每行一个,ai应该等于第i个城市的最终资产价值。


思路:

这道题比较简单,想到是区间加减,可以将原数组变成差分数组,将区间改变为单点修改,也就是说[l,r]区间里-> l点+c,r+1点-c
最后再将修改后的单点前缀和得到最终的结果。


代码:

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

C - 平衡字符串

题意:

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

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

Output
一个整数表示答案


思路:

尺取法维护双指针,一般所求答案为一个连续区间,区间两端能不能明确方向,很显然这道题是可以了这个字符串可以当作一个连续区间,可以利用尺取法来判断是否满足要求。首先设置(L,R)(初始L=R=0),先分别用4个计数来遍历字符串分别统计QWER的个数。如果四个计数都相等,则直接判定字符串平衡,若不想等,先判断第一个字符,并将该字符的计数-1。进行循环,通过替换使得四个计数都相等MAx=max(sum1,sum2,sum3,sum4)。
然后看[L,R]中剩余的字符串数量也就是FREE=(R+L-1)-[(MAx-sum1)+(MAx-sum2)+(MAx-sum3)+(MAx-sum4)]是否大于等于0是4的正倍数。
如果是,满足要求,L++,否则不满足要求R++;直到R < n为止;


代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
string s;
int sum1 = 0,sum2=0,sum3=0,sum4=0;
int l=0, r=0;

int main()
{
	int count = 1000000;
	ios::sync_with_stdio(false);
	cin >> s;
	for (int i = 0; i < s.size(); i++)
	{
		if (s[i] == 'Q')
			sum1++;
		if (s[i] == 'W')
			sum2++;
		if (s[i] == 'E')
			sum3++;
		if (s[i] == 'R')
			sum4++;
	}
	if (sum1 == sum2 &&sum2== sum3&&sum3== sum4)
	{
		cout << 0 << endl;
		exit(0);
	}
	if (s[0] == 'Q')
		sum1--;
	if (s[0] == 'W')
		sum2--;
	if (s[0] == 'E')
		sum3--;
	if (s[0] == 'R')
		sum4--;
	while (1)
	{
		int MAX1 = max(sum1, sum2), MAX2 = max(sum3, sum4);
		int MAX = max(MAX1, MAX2);
		int TOTAL = r- l + 1;
		int FREE = TOTAL - ((MAX - sum1) + (MAX - sum2) + (MAX - sum3) + (MAX-sum4));
		if (FREE >= 0 && FREE % 4 == 0)
		{
			count = min(TOTAL, count);
			if (s[l] == 'Q')
				sum1++;
			if (s[l] == 'W')
				sum2++;
			if (s[l] == 'E')
				sum3++;
			if (s[l] == 'R')
				sum4++;
			l++;
		}
		else
		{
			if (r + 1 >= s.size()) break;
			r++;
			if (s[r] == 'Q') 
				sum1--;
			if (s[r] == 'W') 
				sum2--;
			if (s[r] == 'E')
				sum3--;
			if (s[r] == 'R')
				sum4--;

		}


	}
	cout << count << endl;
	return 0;
	
	
	
}

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


思路:

既然要维护窗口并且从每一个窗口取出最大和最小值,首先先对k-1个维护一个单调递增队列,然后从第k个开始,从k到n继续维护一个单调递增队列,队列中元素属于当前窗口,并且维护的过程中若元素不属于当前窗口,弹出队首元素,每个窗口的最小值就是队首元素,保存至另外一个数组当中。最大值的求法只需将单调递增队列变为维护单调递减队列即可,其他的不变。


代码:

#include<iostream>
#include<deque>
#include<algorithm>
using namespace std;
deque<int> q;
int a[1000001],mi[1000001],ma[1000001];
int main()
{
	int n, k;
	scanf("%d %d",&n,&k);
	for (int i = 1; i <= n; i++)
		scanf("%d",&a[i]);
	for (int i = 1; i <= k - 1; i++)
	{
		while (q.size() && a[q.back()] > a[i])
			q.pop_back();
		q.push_back(i);
	}
	for (int i = k; i <= n; i++)
	{
		while (q.size() && a[q.back()] > a[i])
			q.pop_back();
		q.push_back(i);
		while (q.size() && (i - q.front()) >= k)
			q.pop_front();
		mi[i] = a[q.front()];
	}
	while (!q.empty())
	{
		q.pop_back();
	}
	for (int i = 1; i <= k - 1; i++)
	{
		while (q.size() && a[q.back()] < a[i])
			q.pop_back();
		q.push_back(i);
	}
	for (int i = k; i <= n; i++)
	{
		while (q.size() && a[q.back()] < a[i])
			q.pop_back();
		q.push_back(i);
		while (q.size() && (i - q.front()) >= k)
			q.pop_front();
		ma[i] = a[q.front()];
	}
	for (int i = k; i <=n ; i++)
		printf("%d ",mi[i]);
	printf("\n");
	for (int i = k; i <=n; i++)
		printf("%d ",ma[i]);
	return 0;
}
发布了7 篇原创文章 · 获赞 0 · 访问量 121

猜你喜欢

转载自blog.csdn.net/weixin_44465341/article/details/105105451