暑假考试的一些好题和题解

7.11

考得撇,不想写

7.12

过了 T1,所以决定写 T1 题解

T1

题目大意

给你一个数组,然后你有一个栈,现在要把数组里面的数按照数组内的顺序放进栈里,求最大的出栈序列。

题解

显然可以贪心,字典序基本可以往贪心上靠,因为字典序,所以前面的数一定要大,这是和后面的数没有关系的,满足局部最优全局最优的条件,于是就可以贪心。

因为栈的大小是无限的,考虑从头开始扫,边扫边进栈,每次扫到目前还没有进栈的数的最大值,就把这个最大的数入栈再出栈,所有的数扫完了再把栈里面没有出来的数也弹出来,显然这样子的字典序一定是最大的。

#include <bits/stdc++.h>

using namespace std;

int read() {
	int x = 0, f = 1; char ch = getchar();
	while (ch < '0' || ch > '9') f = ch == '-' ? -1 : 1, ch = getchar();
	while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	return x * f;
}

const int N = 1E6 + 10;

int n, mx, a[N];
stack<int> s;

int main() {
//	freopen("sort.in", "r", stdin);
//	freopen("sort.out", "w", stdout);
	
	n = read();	stack<int> s; int pos = -1;
	for (int i = 1; i <= n; i++) {
		a[i] = read();
		if (a[i] == n) pos = i;
	}
	
	vector<int> ans; ans.push_back(n);
	for (int i = 1; i < pos; i++) s.push(a[i]);
	
	int r = pos + 1;
	while (ans.size() != n) {
		int mx = -1, pos; for (int i = r; i <= n; i++) mx = max(mx, a[i]);
		for (int i = r; i <= n; i++) if (mx == a[i]) pos = i;
		if (!s.size()) {
			ans.push_back(mx);
			for (int i = r; i <= pos - 1; i++) s.push(a[i]);
			r = pos + 1;
		} else {
			if (mx > s.top()) {
				ans.push_back(mx);
				for (int i = r; i <= pos - 1; i++) s.push(a[i]);
				r = pos + 1;
			} else {
				ans.push_back(s.top());
				s.pop();
			}
		}
		
	}
	
	for (int i = 0; i < n; i++) {
		printf("%d ", ans[i]);
	}
	
	return 0;
}s

T2

就开始玄学了。

题意

给定 \(n,m\)
从前有个括号序列 s,满足 \(|s|=m\)
你需要统计括号序列对 \((p,q)\) 的数量。
其中 \(p,q\) 满足 \(|p|+|s|+|q|=n\),且 \(p+s+q\) 是一个合法的括号序列

题解

考试的时候完全没有思路。 知道是一个 dp,但是不知道从何下手。
对于这样要求合法括号序列的问题,考虑两种方法,第一是用栈来维护合法,第二则是定 "\((\)" 为 \(1\), "\()\)" 为 \(-1\),合法就要求这个数组的前缀时时刻刻都要大于等于 \(0\)

然后就可以开始考虑 dp 了,这里我们用上面的第二种思路,考虑后效性的东西,第一个肯定是长度,第二个是上面所说的对应值,设 \(dp_{i,j}\) 为长度为 \(i\),对应值为 \(j\) 的括号序列个数。

可以写出状态转移为

\[dp_{i,j}=dp_{i-1,j-1}+dp_{i-1,j+1} \]

显然,就是放 \((\) 或放 \()\) 的方案加起来。

因为是从 \(i-1\) 转移过来的,所以直接普通的循环搞就行了。

然后统计答案就像题目描述一样,分成三段,枚举第一段的长度,第二段的长度是给出的 \(m\),第三段的长度是所有的 \(n\) 减去 \(m\) 和第一段的长度。然后枚举对应值,为了让总的括号序列是合法的,先处理出中间第二段最小的对应值,只要第一段的值比这个最小的值大就可以了,第三段的对应值显然就是第一段的对应值(枚举的 \(j\) ) 加上第二段的对应值 (也可以预处理)。

注意初始化和边界处理,以及特殊情况的转移。

#include <bits/stdc++.h>
#define i64 long long

using namespace std;

int read() {
	int x = 0, f = 1; char ch = getchar();
	while (ch < '0' || ch > '9') f = ch == '-' ? -1 : 1, ch = getchar();
	while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	return x * f;
}
// #define check
const int N = 100010;
const int P = 1E9 + 7;	

int a[N + 1];
char ch[N + 1];

// ( -> 1 ) -> -1; 
// dp[i][j] 表示长度为 i, 对应的值为 j 的方案数量
 
i64 dp[2010][2010];
int mn = 1E9;
int main() {
	#ifdef check
	freopen("a.in", "r", stdin);
	#endif
	
	int n = read(), m = read(); 
	scanf("%s", ch + 1);
	
	for (int i = 1; i <= m; i++) {
		a[i] = a[i - 1] + (ch[i] == '(' ? 1 : -1);
		mn = min(mn, a[i]);
	}
	
	dp[0][0] = dp[1][1] = 1;
	for (int i = 2; i <= n - m; i++) {
		dp[i][0] = dp[i - 1][1]; 
		for (int j = 1; j <= n - m; j++)
			dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j + 1]) % P;
	}
		
	
	i64 ans = 0;
	for (int i = -mn; n - i - m >= 0; i++)
		for (int j = -mn; j <= i; j++)
			if (j + a[m] >= 0)
				ans = (ans + 1LL * dp[i][j] * dp[n - i - m][j + a[m]] % P) % P;		
			
	
	printf("%lld\n", ans % P);
	
	return 0;
}

T3 T4 至今不会。

8.7

posted @ 2023-08-11 16:43  落花月朦胧  阅读(28)  评论(0编辑  收藏  举报