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

本题难度较大,因为操作次数和前一个高度差值有关,所以如果要改变某个积木,那么改成和上一个积木高度一样是最好的。因为这样可以将两个积木看成一块积木同时操作,一定不会让操作次数变多。

  1. \(k = 0\) 时,每一块积木对答案的贡献为 \(\max(0, h_i-h_{i-1})\)

  2. \(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;
}