暑假考试的一些好题和题解
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\) 的括号序列个数。
可以写出状态转移为
显然,就是放 \((\) 或放 \()\) 的方案加起来。
因为是从 \(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 至今不会。