2 条题解

  • 0
    @ 2026-1-28 14:22:06

    看着感觉无法贪心,观察发现数据范围较小,可以n方DP。 每个位置要么逆序要么不逆序,DP前缀以这个位置结尾的最小代价,分是否逆序,状态转移直接暴力枚举上一个是啥。

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    
    const int INF = 0x3f3f3f3f3f3f3f3f;
    
    void sol(){
        int n, c1, c2; cin >> n >> c1 >>c2;
        vector<int> a(n + 5), b(n + 5);
        vector<int> dp(n + 5, INF), dpf(n + 5, INF);
        for(int i = 1; i <= n; i ++)    cin >> a[i] >> b[i];
        a[0] = b[0] = 0;
        dp[0] = dpf[0] = 0;
        for(int i = 1; i <= n; i ++){
            for(int j = 0; j < i; j ++){
                if(a[i] >= a[j] && b[i] >= b[j]){
                    dp[i] = min(dp[i], (i - j - 1) * c1 + dp[j]);
                    dpf[i] = min(dpf[i], (i - j - 1) * c1 + dpf[j] + c2);
                }
                if(a[i] >= b[j] && b[i] >= a[j]){
                    dp[i] = min(dp[i], (i - j - 1) * c1 + dpf[j]);
                    dpf[i] = min(dpf[i], (i - j - 1) * c1 + dp[j] +c2);
                }
            }
        }
        int res = INF;
        for(int i = 0; i <= n; i ++){
            int t = min(dp[i], dpf[i]);
            res = min(res, (n - i) * c1 + t);
        }
        cout << res << endl;
    }
    
    signed main(){
        int t; cin >> t;
        while(t --)
            sol();
    }
    
    • 0
      @ 2025-12-16 17:29:01

      前置知识:使用动态规划求解最长上升子序列。

      首先一个合法序列需要满足的要求其实就是 aiai1,bibi1a_i\ge a_{i-1},b_i\ge b_{i-1},因此这很像最长上升子序列,回忆一下,最长上升子序列的状态定义是 dp[x]dp[x] 表示以 xx 处的数字结尾的最长上升子序列的长度。

      我们想类似定义dp数组,但发现还有一个交换操作,但其实交换也并不麻烦、只会多出每个位置一个状态,即我们可以定义:dp[x][0/1]dp[x][0/1] 表示以位置 xx 处作为结尾、位置 xx 处是否交换(第二维为 11 表示交换)时所对应的最长合法序列。转移时假设当前位置为 ii,枚举上一个位置 jj,如果能把 ai,bia_i,b_i(或bi,aib_i,a_i)接到 aj,bja_j,b_j(或bj,ajb_j,a_j)后面,则可以花费(ij1)c1(i-j-1)c_1来删除两者中间的那段。

      • 1

      信息

      ID
      161
      时间
      1000ms
      内存
      256MiB
      难度
      6
      标签
      递交数
      20
      已通过
      5
      上传者