1 条题解

  • 0
    @ 2026-10-26 22:50:06

    思路与算法

    枚举r行集合;固定行后预计算每列的纵向代价和任意两列相邻代价,再做选c列且保持顺序的最小代价DP。

    正确性说明

    每种合法子矩阵有唯一的行集合,枚举不遗漏。固定行后总分可分为所选各列纵向代价与相邻选列横向代价,按最后一列和已选列数转移恰好各计一次,取最小得到该行集合最优,再全局取小。

    复杂度

    O(C(n,r)(rm²+cm²)+2^n)时间、O(cm+m²+n*m)空间,含每组行枚举的代价表与DP初始化。

    易错点

    上下相邻指所选矩阵中的相邻行,不是原矩阵相邻行;r或c为1时对应方向贡献为0。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        int n, m, r, c;
        cin >> n >> m >> r >> c;
        vector<vector<int>> a(n, vector<int>(m));
        for (auto &row : a)
            for (int &x : row)
                cin >> x;
        int ans = INT_MAX;
        for (int mask = 0; mask < (1 << n); mask++)
            if (__builtin_popcount((unsigned)mask) == r) {
                vector<int> row;
                for (int i = 0; i < n; i++)
                    if (mask >> i & 1)
                        row.push_back(i);
                vector<int> v(m);
                vector<vector<int>> h(m, vector<int>(m));
                for (int j = 0; j < m; j++) {
                    for (int i = 1; i < r; i++)
                        v[j] += abs(a[row[i]][j] - a[row[i - 1]][j]);
                    for (int k = 0; k < j; k++)
                        for (int i : row)
                            h[k][j] += abs(a[i][k] - a[i][j]);
                }
                vector<vector<int>> dp(c + 1, vector<int>(m, 1000000000));
                for (int j = 0; j < m; j++)
                    dp[1][j] = v[j];
                for (int k = 2; k <= c; k++)
                    for (int j = k - 1; j < m; j++)
                        for (int p = k - 2; p < j; p++)
                            dp[k][j] = min(dp[k][j], dp[k - 1][p] + h[p][j] + v[j]);
                ans = min(ans, *min_element(dp[c].begin(), dp[c].end()));
            }
        cout << ans << "\n";
    }
    
    • 1

    信息

    ID
    1908
    时间
    1000ms
    内存
    128MiB
    难度
    普及
    标签
    递交数
    0
    已通过
    0
    上传者