Mobile wallpaper 1Mobile wallpaper 2Mobile wallpaper 3Mobile wallpaper 4Mobile wallpaper 5
1250 字
6 分钟
CSES Dynamic Programming
2026-01-06
统计加载中...

这些是寒假集训新做的题,之前做过的不会再专门写一遍题解了,如果需要代码可以看仓库

Longest Common Subsequence#

经典的 LCS,一个二维 DP 就可以解决,同时需要统计一下前驱,横着走和竖着走的时候不在 LCS 上,斜着走的时候在 LCS 上,最后输出。

#include <iostream>
using namespace std;
const int N = 1010;
int f[N][N];
pair<int, int> pre[N][N];
int a[N], b[N];
void print(int i, int j) {
if (!i) return;
print(pre[i][j].first, pre[i][j].second);
if (pre[i][j] == make_pair(i - 1, j - 1)) cout << a[i] << ' ';
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
for (int i = 1; i <= m; ++i) {
cin >> b[i];
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (a[i] == b[j]) f[i][j] = f[i - 1][j - 1] + 1, pre[i][j] = {i - 1, j - 1};
else if (f[i - 1][j] > f[i][j - 1]) f[i][j] = f[i - 1][j], pre[i][j] = {i - 1, j};
else f[i][j] = f[i][j - 1], pre[i][j] = {i, j - 1};
}
}
cout << f[n][m] << endl;
print(n, m);
cout << endl;
return 0;
}

Minimal Grid Path#

刚开始只想着怎么 DP 出来一条路径,然后不是 TLE 就是 MLE,后来才发现事情远没有我想象的那么复杂。

从左上角开始贪心的选最小的,同步更新所有疑似是答案的路径。可行的 O(n2)O(n^2) 做法有沿着主对角线方向一层一层手动更新或者用单调队列bfs。我用了第一种,MoScenix用了第二种。

#include <iostream>
using namespace std;
const int N = 3010;
char a[N][N];
bool v[N][N];
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1; i <= n; ++i) cin >> (a[i] + 1);
v[1][1] = true;
cout << a[1][1];
for (int k = 2; k < n * 2; ++k) {
char mn = 'Z' + 1;
for (int i = 1; i <= k; ++i) {
int j = k - i;
if (i < 0 || i > n || j < 0 || j > n) continue;
if (!v[i][j]) continue;
if (i < n) mn = min(mn, a[i + 1][j]);
if (j < n) mn = min(mn, a[i][j + 1]);
}
cout << mn;
for (int i = 1; i <= k; ++i) {
int j = k - i;
if (i < 0 || i > n || j < 0 || j > n) continue;
if (!v[i][j]) continue;
if (a[i + 1][j] == mn) v[i + 1][j] = true;
if (a[i][j + 1] == mn) v[i][j + 1] = true;
}
}
cout << endl;
return 0;
}

Mountain Range#

根据直觉这个可以巧妙的转化成一个用一个中序遍历的序列生成一个二叉堆,求最大深度。这个问题就相当于在二叉堆上从上到下走到底的路径长度,需要注意如果一个节点和父亲节点权值一样那么这一条边不计入深度,因为不能平飞,用线段树维护出来区间最大值的位置就可以 O(nlogn)O(n\log n) 处理完。

#include <iostream>
#include <vector>
#include <tuple>
using namespace std;
typedef pair<int, int> PII;
const int N = 200010;
PII tr[N * 4];
int n;
PII merge(PII a, PII b) {
if (a.first >= b.first) return a;
else return b;
}
void pushup(int u) {
tr[u] = merge(tr[u << 1], tr[u << 1 | 1]);
}
void modify(int u, int l, int r, int p, int v) {
if (l == r) tr[u] = {v, l};
else {
int mid = l + r >> 1;
if (p <= mid) modify(u << 1, l, mid, p, v);
else modify(u << 1 | 1, mid + 1, r, p, v);
pushup(u);
}
}
PII query(int u, int l, int r, int ql, int qr) {
if (ql > qr) {
cout << "AWA" << endl;
}
if (ql <= l && r <= qr) return tr[u];
else {
int mid = l + r >> 1;
PII res = {-1, -1};
if (ql <= mid) res = merge(res, query(u << 1, l, mid, ql, qr));
if (qr > mid) res = merge(res, query(u << 1 | 1, mid + 1, r, ql, qr));
return res;
}
}
int dp(PII rt, int l, int r) {
if (l == r) return 1;
int res = 1;
if (l <= rt.second - 1) {
PII ls = query(1, 1, n, l, rt.second - 1);
res = max(res, dp(ls, l, rt.second - 1) + (rt.first > ls.first));
}
if (rt.second + 1 <= r) {
PII rs = query(1, 1, n, rt.second + 1, r);
res = max(res, dp(rs, rt.second + 1, r) + (rt.first > rs.first));
}
return res;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i) {
int t;
cin >> t;
modify(1, 1, n, i, t);
}
cout << dp(tr[1], 1, n) << endl;
return 0;
}

Increasing Subsequence II#

也是比较经典的题,从求长度变成了求个数。初始化一个末尾是 0 的子序列个数是 1,离散化一下权值,用树状数组维护动态前缀和统计方案数。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 200010, MOD = 1000000007;
typedef long long LL;
vector<int> values;
int a[N], m;
LL tr[N];
int get(int x) {
return lower_bound(values.begin(), values.end(), x) - values.begin() + 1;
}
void add(int x, LL v) {
for (; x <= m; x += x & -x) {
tr[x] = (tr[x] + v) % MOD;
}
}
LL query(int x) {
LL res = 0;
for (; x; x -= x & -x) {
res = (res + tr[x]) % MOD;
}
return res;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
values.emplace_back(a[i]);
}
values.emplace_back(0);
sort(values.begin(), values.end());
values.erase(unique(values.begin(), values.end()), values.end());
m = values.size();
for (int i = 1; i <= n; ++i) a[i] = get(a[i]);
add(1, 1);
LL res = 0;
for (int i = 1; i <= n; ++i) {
res = (res + query(a[i] - 1)) % MOD;
add(a[i], query(a[i] - 1));
}
cout << res << endl;
return 0;
}
CSES Dynamic Programming
https://starlab.top/posts/cses-dp/
作者
Star
发布于
2026-01-06
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时