week5(单调栈+单调队列+尺取法+差分)

A - 最大矩形

题目

样例输入

7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0

样例输出

8
4000

 

思路

以单调递增栈来说,就是从栈顶到栈底元素单调递增,元素入栈时如果比栈顶元素大,则将栈顶元素出栈直至栈顶元素比入栈元素大。如果将一个数组依次入栈,对于由于元素入栈而被迫出栈的元素来讲,入栈元素就是第一个比他们大的元素。单调递减栈同理。

模拟单调递增栈入栈过程:

[10]           10 入栈时, 栈空, 入栈    

[3, 10]       3 入栈时, 栈顶元素 10 大于 3, 入栈 

[7, 10]       7入栈时, 栈顶元素 3 小于 7, 弹栈; 栈顶元素 10 大于 7, 入栈

[4, 7 ,10]   4 入栈时, 栈顶元素 7 大于 4, 入栈

[12]          12 入栈时, 栈顶元素 4 小于 12, 弹栈; 栈顶元素 7 小于 12, 弹栈; 栈顶元素 10 小于 12, 弹栈; 栈空, 入栈

 

这道题需要算出每个高度不同矩形的最大面积。假设l是其中一个高度为h的矩形的左边界,r是右边界。那么对于这个矩形来讲,l是左边第一个低于h的坐标,r是右边第一个低于h的坐标。对于l,r可通过单调递减栈求解(数组正向求解一次,反向求解一次)。

 

代码

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

struct s
{
	int l;
	int r;
	long long h;
};

int main()
{
	int n;
	while (1)
	{
		cin >> n;
		if (n == 0) break;
		long long *a = new long long[n];
		long long *b = new long long[n];
		struct s *c = new s[n];
		for (int i = 0; i < n; i++)
		{
			cin >> a[i];
			b[n - 1 - i] = a[i];
		}
		stack<pair<int, long long> > q;
		for (int i = 0; i < n; i++)
		{
			while (!q.empty() && q.top().second > a[i])
			{
				c[q.top().first].r = i;				
				c[q.top().first].h = q.top().second;
				q.pop();
			}
			pair<int, long long> p1(i, a[i]);
			q.push(p1);	
		}
		while (!q.empty())
		{
			c[q.top().first].r = n;
			c[q.top().first].h = q.top().second;
			q.pop();
			
		}

		for (int i = 0; i < n; i++)
		{
			while (!q.empty() && q.top().second > b[i])
			{
				c[n - 1 - q.top().first].l = n - 1 - i;
				q.pop();
			}
			pair<int, long long> p1(i, b[i]);
			q.push(p1);
		}
		while (!q.empty())
		{
			c[n - 1 - q.top().first].l = -1;
			c[n - 1 - q.top().first].h = q.top().second;
			q.pop();
			
		}

		long long max = 0;
		for (int i = 0; i < n; i++)
		{
			long long ss = (c[i].r - c[i].l - 1) * c[i].h;
			if (ss > max)
				max = ss;
		}
		cout << max << endl;
	}
}

 

 

 

 

B - TT's Magic Cat

题目

多亏了上周大家的帮助,它终于得到了一只可爱的猫。但没想到的是,这是一只神奇的猫。

有一天,神奇的猫决定调查TT的能力,给他一个问题。即从世界地图中选择n个城市,a[i]表示第i个城市拥有的资产价值。

然后,这只神奇的猫将执行几项操作。每轮选择[l,r]区间内的城市,并将其资产价值增加c,最后需要给出q操作后各城市的资产价值。

你能帮我找到答案吗?

输入

第一行包含两个整数n q(1≤n,q≤2e5)-城市数目和业务。

第二行包含序列a的元素:整数a1,a2,...,an(−1e6≤ai≤1e6).

接下来是q行,每一行代表一个操作。第i行包含三个整数l,r和c(1≤l≤r≤n,−1e5≤c≤1e5) ,对于第i次操作。

输出

打印n个整数a1,a2,…,an,每一行一个,i应该等于第i个城市的最终资产价值。

样例输入1

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

样例输出1

-3 6 9 2

样例输入2

2 1
5 -2
1 2 4

样例输出2

9 2

样例输入3

1 2
0
1 1 -8
1 1 -6

样例输出3

-14

 

思路

暴力解决的话时间复杂度为O(nq),但我们可以考虑使用差分数组来解决。

原数组为a,差分数组c(两个数组范围均为[1, n]),c[1] = a[1],c[i] = a[i] - a[i - 1];

按照这种构造方法,每次操作只需要c[l] += c, c[r + 1]  -= c,将时间复杂度降到了O(n+q)。

 

代码

#include <iostream>
#include <cstring>
using namespace std;
long long m, n;

int main()
{
	cin >> n >> m;
	long long *a = new long long[n + 1];
	long long *b = new long long[n + 1];
	memset(a, 0, sizeof a);
	memset(b, 0, sizeof b);
	a[0] = 0;
	for (long long i = 1; i <= n; i++)
	{
		cin >> a[i];
		b[i] = a[i] - a[i - 1];
	}

	for (long long i = 0; i < m; i++)
	{
		long long l, r, value;
		cin >> l >> r >> value;
		b[l] += value;
		if (r < n)
			b[r + 1] -= value;

	}
	for (long long i = 1; i <= n; i++)
	{
		a[i] = b[i] + a[i - 1];
		cout << b[i] + a[i - 1] << " ";
	}
		
	//system("Pause");
}

 

 

 

 

C - 平衡字符串

题目

 

思路

本道题采用尺取法。

所谓尺取法,在一个数组中维护两个指针 l 和 r ,起始时 l = r = 1(假设数组范围为[1, n]),

如果此时 [l, r] 中元素满足要求,l++;如果不满足要求,r++。

本题的要求就是使用 [l, r] 中的字符在将范围外的字符填充至数量相等时,剩余元素数为4的倍数。

MAX = max(sum1, sum2, sum3, sum4),sum表示个字符在范围外的总数

TOTAL = R – L + 1  ,范围内字符个数

FREE = TOTAL - [(MAX-sum1)+(MAX-sum2)+(MAX-sum3)+(MAX-sum4)],除去填充字符外的剩余字符。

分析化简可知,[(MAX-sum1)+(MAX-sum2)+(MAX-sum3)+(MAX-sum4)] = 4 * MAX - N + TOTAL,N为字符串中的字符总数。

 

代码

#include <iostream>
#include <algorithm>
using namespace std;
char c[110000];


int main()
{
	cin >> c;
	int n = strlen(c);
	int q = 0;
	int w = 0;
	int e = 0;
	int r = 0;
	for (int i = 0; i < n; i++)
	{
		if (c[i] == 'Q') q++;
		if (c[i] == 'W') w++;
		if (c[i] == 'E') e++;
		if (c[i] == 'R') r++;
	}
	if (q == w && w == e && e == r) cout << 0 << endl;
	else
	{
		int maxx = 0;
		int l = 0;
		int rr = 1;
		int min = n;
	
		//cout << "n:" << n << endl;
		while (1)
		{
			int mm = rr - l;
			int sub1 = 0;
			int sub2 = 0;
			int sub3 = 0;
			int sub4 = 0;
			for (int i = l; i < rr; i++)
			{
				if (c[i] == 'Q') sub1++;
				if (c[i] == 'W') sub2++;
				if (c[i] == 'E') sub3++;
				if (c[i] == 'R') sub4++;
			}

			int maxx = max(max(q - sub1, w - sub2), max(e - sub3, r - sub4));
			int total = 4 * maxx - n + mm;
			int re = mm - total;
			if (rr == n && (re < 0 || re % 4 != 0))
				break;
			if (re >= 0 && re % 4 == 0)
			{
				if (mm < min) min = mm;
				l++;
			}
			else rr++;	
		}
		cout << min << endl;
	}
	
	system("Pause");
}

 

 

 

 

D - 滑动窗口

题目

样例输入

8 3
1 3 -1 -3 5 3 6 7

样例输出

-1 -3 -3 -3 3 3
3 3 5 5 6 7

 

思路

采用单调队列的方法,使用了deque双向队列。

队尾维护单调性,以单调递增队列举例,如果队尾元素出队列,直到队尾元素小于入队元素。

队首元素维护窗口的特性,每次入队的是数组的序号,如果序号小于左边界,则出队列,直到序号大于左边界。

那么队列中间的元素有可能序号小于左边界吗?

答案是否定的,因为在这种情况下,假设队列中序号小于左边界的元素为a[ i ],队首元素序号大于左边界,元素小于a[ i ]。事实上这种情况不可能出现,因为队首元素的序号大于i,即在 i 之后入队,而它的元素值又小于a[ i ],那么在它入队列时会将 i 挤出队列,因此这种情况不可能出现。

寻找最大元素维护一个递减队列,最小元素维护一个递增队列。

 

#include <iostream>
#include <deque>
#include <cstdio>
using namespace std;

deque<int> q;
int a[1000100];
int minn[1000100];
int maxx[1000100];

int main()
{
	int n, k;
	scanf_s("%d%d", &n, &k);
		
	for (int i = 0; i < n; i++)
		scanf_s("%d", &a[i]);

	for (int i = 0; i < k; i++)
	{
		while (!q.empty() && a[q.back()] >= a[i])
			q.pop_back();
		q.push_back(i);
	}
	minn[0] = a[q.front()];

	for (int i = k; i < n; i++)
	{
		while (!q.empty() && q.front() <= (i - k))
			q.pop_front();
		while (!q.empty() && a[q.back()] >= a[i])
			q.pop_back();

		q.push_back(i);
		minn[i - k + 1] = a[q.front()];
	}
	q.clear();

	for (int i = 0; i < k; i++)
	{
		while (!q.empty() && a[q.back()] <= a[i])
			q.pop_back();
		q.push_back(i);
	}
	maxx[0] = a[q.front()];

	for (int i = k; i < n; i++)
	{
		while (!q.empty() && q.front() <= (i - k))
			q.pop_front();
		while (!q.empty() && a[q.back()] <= a[i])
			q.pop_back();

		q.push_back(i);
		maxx[i - k + 1] = a[q.front()];
	}


	for (int i = 0; i < n - k + 1; i++)
	{
		printf("%d", minn[i]);
		printf(" ");
	}

	cout << endl;
	for (int i = 0; i < n - k + 1; i++)
	{
		printf("%d", maxx[i]);
		printf(" ");
	}
	cout << endl;
	system("Pause");
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值