「程序设计思维与实践」Week5 作业:A - 最大矩形、B - TT's Magic Cat、D - 滑动窗口。单调栈+前缀和+尺取法+单调队列

A - 最大矩形 HDU - 1506

题目描述

给一个直方图,求直方图中的最大矩形的面积。例如,下面这个图片中直方图的高度从左到右分别是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
题解
  • 思路:对于每一个柱形的高找到最优的面积,即左右都达到最宽。朴素方法为从此柱形开始左右都寻找一遍复杂度是n^2,而单调栈,可以在遍历的过程中,通过维护一个单调关系,当单调不合法时,记录弹出元素的左右端点,将复杂度降为O(n).
  • 具体实现为:左右都跑一边,记录每个柱形的最左和最右,最后高度乘以r-l+1取所有max,就是最后答案。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
using namespace std;

typedef long long LL;
const int MAXN = 100000+10;
int h[MAXN], l[MAXN], r[MAXN];

int main() {
	int n;
	while (cin >> n) {
		if (!n) break;
		for (int i = 0; i < n; ++i) {
			scanf("%d", &h[i]);
		}
		stack<int> s;
		s.push(0);
		for (int i = 1; i < n; ++i) {
			while (s.size() && h[i] < h[s.top()]) {
				r[s.top()] = i;
				s.pop();
			}
			s.push(i);
		}
		while (s.size()) {
			r[s.top()] = n;
			s.pop();
		}

		s.push(n-1);
		for (int i = n-2; i >= 0; --i) {
			while (s.size() && h[i] < h[s.top()]) {
				l[s.top()] = i;
				s.pop();
			}
			s.push(i);
		}
		while (s.size()) {
			l[s.top()] = -1;
			s.pop();
		}
		LL ans = 0;
		for (int i = 0; i < n; ++i) {
			// cout << l[i] << " " << r[i] << endl;
			ans = max(ans, (LL)((LL)h[i]*(LL)(r[i]-l[i]-1)));
		}
		cout << ans << endl;
	}
	return 0;
}

B - TT’s Magic Cat Gym - 272542A

题目描述

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 n cities from the world map, and a[i] represents the asset value owned by the i-th city.
Then the magic cat will perform several operations. Each turn is to choose the city in the interval [l,r] and increase their asset value by c. And finally, it is required to give the asset value of each city after q operations.
Could you help TT find the answer?

input

在这里插入图片描述

Output

Print n integers a1,a2,…,an one per line, and ai should be equal to the final asset value of the i-th city.

Sample Input 1
4 2
-3 6 8 4
4 4 -2
3 3 1
Sample Output 1
-3 6 9 2
Sample Input 2
2 1
5 -2
1 2 4
Sample Output 2
9 2
Sample Input 3
1 2
0
1 1 -8
1 1 -6
Sample Output 3
-14
题解
  • 思路:每次操作如果暴力遍历复杂度是n^2不可行,考虑到每次操作都是对连续区间的加减操作我们可以考虑:能否快速的把一次修改区间的操作表示出来?因为每次操作都是独立的,而且加减操作没有顺序性,我们完全可以分别记录然后最后累加所有的状态。另一方面,对于一个区间内的状态,我们记录一个标记,这个标记当遍历到区间左端点开启,右端点关闭,这样就O(1)记录了整个区间的变化。
  • 实现:先根据输入给区间加标记,即左闭为变化值,右开抵消变化值,最后取标记的前缀和即可。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
using namespace std;

typedef long long LL;
const int MAXN = 200000+10;
LL a[MAXN], c[MAXN];

int main() {
	int n, q;
	cin >> n >> q;
	for (int i = 1; i <= n; ++i) {
		scanf("%lld", &a[i]);
	}
	while (q--) {
		int l, r;
		LL ci;
		scanf("%d%d%lld", &l, &r, &ci);
		c[l] += ci;
		c[r+1] -= ci;
	}
	c[0] = 0;
	for (int i = 1; i <= n; ++i) {
		c[i] += c[i-1];
		printf("%lld ", a[i]+c[i]);
	}
	return 0;
}

C - 平衡字符串 Gym - 270737B

题目描述

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

input

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

Output

一个整数表示答案

Example
Input
QWER
Output
0
Input
QQWE
Output
1
Input
QQQW
Output
2
Input
QQQQ
Output
3
题解
  • 对于已知的 qwer 的个数,想要变成合法的序列,那么我们需要把小的部分像大的部分标齐,而我们判断一个区间是否能用来把qwer标齐只需要判断其个数是否能够达到用来填补的个数。这样在区间遍历的过程中很简单的就可以维护qwer的个数,又能得知当前区间的长度。所以可以用尺取法。
  • 实现:qwer分别记录「除了当前选中的区间」之外qwer的个数。采用尺取法的两个步骤:1.右端点往右找到第一个合法的右端点,2.左端点不断朝右缩小,直到满足合法情况长度最小,记录答案即可。判断合法的过程:计算当前区间长度与区间外需要的填补的长度,如果大于且能够是4的倍数则合法。4的倍数的原因:填补之后如果有剩余,刚好再增加一组qwer。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
using namespace std;

const int MAXN = 100000+10;
char s[MAXN];
int q, w, e, r;

bool check(int L, int R) {
	// cout << "--------- " << l << "," << r << endl;
	// cout << "qwer" << q << " " << w << " " << e << " " << r << endl;
	int theMax = max(q,w);
	theMax = max(e,theMax);
	theMax = max(r,theMax);
	int need = 4*theMax-q-w-e-r;
	int now = R-L+1;
	int free = now-need;
	// cout << now << ", " << need << endl;
	if (free >= 0 && free%4 == 0) return true;
	return false;
}

int main() {
	scanf("%s", s);
	int len = strlen(s);
	q = w = e = r = 0;
	for (int i = 0; i < len; i++) {
		if (s[i] == 'Q') q++; 
		else if (s[i] == 'W') w++; 
		else if (s[i] == 'E') e++; 
		else if (s[i] == 'R') r++; 
	}
	int ans = len;

	if (check(0,-1)) {
		ans = 0;
		cout << ans << endl;
		return 0;
	}

	if (s[0] == 'Q') q--;
	else if (s[0] == 'W') w--; 
	else if (s[0] == 'E') e--; 
	else if (s[0] == 'R') r--; 

	int L = 0, R = 0;
	while (R < len) {
		while(L <= R && check(L,R)) {
			ans = min(ans, R-L+1);
			L++;
			if (s[L-1] == 'Q') q++; 
			else if (s[L-1] == 'W') w++; 
			else if (s[L-1] == 'E') e++; 
			else if (s[L-1] == 'R') r++; 
			// cout << "lll " << L << "," << R << endl;
		}
		while (R < len && !check(L,R)) {
			R ++;
			if (s[R] == 'Q') q--;
			else if (s[R] == 'W') w--; 
			else if (s[R] == 'E') e--; 
			else if (s[R] == 'R') r--; 
			// cout << "rrr " << L << "," << R << endl;
		}
		if(R >= len) break;
	}
	cout << ans << endl;

	return 0;
}


D - 滑动窗口 POJ - 2823

题目描述

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

Input

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

Output

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

Sample Input
8 3
1 3 -1 -3 5 3 6 7
Sample Input
-1 -3 -3 -3 3 3
3 3 5 5 6 7
题解
  • 由于最小值和最大值是同一性质的问题,问题可以变为:如果在滑动窗口的过程中维护最值。本题需求:将下一个要移动到的值与现有的值进行比较;将已经滑出的值删除。于是考虑单调栈,既可以维护前端的最值,又可以对后端已经不在窗口范围内的值删除,从而获取了当前区间的最值。
  • 实现:用的stl deque,感觉手动模拟对于数组特殊数据可能会有很大的浪费,复杂的实现又没必要了。单调队列:队列中存储的是元素位置,每次往右移动1,然后计算出此时左端点应该在哪,队列中值在左端点之前的直接删掉;然后处理右侧,将下一个值与队列中已有的进行比较,如果我们要的是最小值,那么就把大于的都删掉,如果要的是最大值,那么把小于的都弹出,最后把这个值推进去。每次移动后队尾的元素就是维护的最值。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
using namespace std;

const int MAXN = 1000000+10;
int n, k;
int a[MAXN];


int main() {
	cin >> n >> k;
	for(int i = 0; i < n; ++i)
		scanf("%d", &a[i]);
	deque<int> q;
	int r = 0;
	for(r = 0; r < k; ++r) {
		while (q.size() && a[r] <= a[q.front()]) {
			q.pop_front();
		}
		q.push_front(r);
	}
	r--;
	cout << a[q.back()] << " ";
	while (r+1 < n) {
		r++;
		int l = r-k+1;
		if (l > q.back()) {
			q.pop_back();
		}
		while (q.size() && a[r] <= a[q.front()]) {
			q.pop_front();
		}
		q.push_front(r);
		cout << a[q.back()] << " ";
	}
	cout << endl;

	while (q.size()) q.pop_front();
	for(r = 0; r < k; ++r) {
		while (q.size() && a[r] >= a[q.front()]) {
			q.pop_front();
		}
		q.push_front(r);
	}
	r--;
	cout << a[q.back()] << " ";
	while (r+1 < n) {
		r++;
		int l = r-k+1;
		if (l > q.back()) {
			q.pop_back();
		}
		while (q.size() && a[r] >= a[q.front()]) {
			q.pop_front();
		}
		q.push_front(r);
		cout << a[q.back()] << " ";
	}
	cout << endl;	


	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值