T1:子序列相似度
本题难度中等,做法和编辑距离类似,用 dp[i][j]
表示 \(s\) 的长为 \(i\) 的前缀和 \(t\) 的长为 \(j\) 的前缀的最大相似度
初值:
\(dp[0][0] = 0\)
转移:
\( dp[i][j]= \begin{cases} dp[i-1][j]\\ dp[i][j-1]\\ dp[i-1][j-1] + 225-26|s_i-s_j| \end{cases} \)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int dp[3005][3005];
inline void chmax(int& x, int y) { if (x < y) x = y; }
int main() {
int n, m;
cin >> n >> m;
string s, t;
cin >> s >> t;
rep(i, n)rep(j, m) {
int now = 0;
chmax(now, dp[i][j+1]);
chmax(now, dp[i+1][j]);
chmax(now, dp[i][j]+225-26*abs(s[i]-t[j]));
dp[i+1][j+1] = now;
}
cout << dp[n][m] << '\n';
return 0;
}
T2:积木大赛2
本题难度较大,因为操作次数和前一个高度差值有关,所以如果要改变某个积木,那么改成和上一个积木高度一样是最好的。因为这样可以将两个积木看成一块积木同时操作,一定不会让操作次数变多。
-
当 \(k = 0\) 时,每一块积木对答案的贡献为 \(\max(0, h_i-h_{i-1})\)
-
当 \(k \neq 0\) 时,修改等价于删除,修改 \(k\) 次等价于删除 \(k\) 次,意味着留下 \(n-k\) 块积木
记 dp[i][j]
表示上一块留下积木 \(i\)且共留下 \(j\) 块积木时的最少操作次数
初值:
\(dp[0][0] = 0\),其他 \(dp = \infty\)
转移:
\( dp[i][j] = \min\limits_{x=0}^{i-1}\{dp[x][j-1] + \max(0, h_i-h_x)\} \)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
using ll = long long;
ll dp[305][305];
inline void chmin(ll& x, ll y) { if (x > y) x = y; }
int main() {
int n, k;
cin >> n >> k;
if (n == k) {
puts("0");
return 0;
}
vector<int> h(n+1);
rep(i, n) cin >> h[i];
memset(dp, 0x3f, sizeof dp);
dp[0][0] = 0;
rep(i, n) {
rep(j, n-k) {
for (int x = 0; x < i; ++x) {
chmin(dp[i][j], dp[x][j-1]+max(0, h[i]-h[x]));
}
}
}
ll ans = 1e18;
rep(i, n) chmin(ans, dp[i][n-k]);
cout << ans << '\n';
return 0;
}