[NOIP2014 普及组] 子矩阵 题解

[NOIP2014 普及组] 子矩阵

题目链接

解法

暴力

先介绍比较暴力的解法,不难想出,我们可以直接暴力搜索每个子矩阵。对于每个子矩阵的贡献,我们求出最小的那个即可。

复杂度为 O((nr)(mc)rc)

大概能得 60 分。

暴力代码

优化

考虑对暴力代码进行优化:注意到行与列的贡献是可以分开算的,二者之间并不会互相影响。所以我们可以先预处理出 sumR[i][j]sumV[i][j] 数组,对于前者,表示对第 i 行,选择列状态为 j 时候的行贡献,后者同理。

复杂度为 O((nr)(mc)max(r,c))

大概能得 80 分。

优化后代码

正解

对于暴力的优化我自己已经想不出更好的了,所以考虑其他写法。

考虑本题的一维状态下的情况,即对于一个数组 a 来说,取 n 个值,问取出来相邻数之间的差的绝对值之和最小。对于这个问题,很容易想到用 dp 的方法去做。我们定义 dp[i][j] 表示前 i 个数选了 j 个(其中 i 是选了的)的最小贡献。转移为 dp[i][j]=minv<idp[v][j1]+|a[i]a[v]|

注意到在本题中行与列之间的贡献不会相互影响,所以我们可以把一行(列)的状态看作一个整体后,转化为一维状态下的问题进行考虑。

dp[i][j][con] 表示前 i 行, 选了 j 个,选的列状态为 con 的情况下的最小贡献。那么我们转移为 dp[i][j][con]=minvdp[v][j1][con]+sum[i][con]+d[v][i][con]

其中 sum[i][con] 表示第 i 行选择列的状态为 con 时的行贡献,d[v][i][con] 表示第 v 行和第 i 行,选择的状态为 con 的对应列元素之间的差绝对值。

复杂度为 O((mc)n2r)

代码
#include <bits/stdc++.h>
#define endl '\n'
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
const int INF = 0x3f3f3f3f, N = 1e5 + 10;
const int MOD = 1e9 + 7;
const double eps = 1e-6;
const double PI = acos(-1);
inline int lowbit(int x) {return x & (-x);}

inline void solve() {
    int n, m, r, c; cin >> n >> m >> r >> c;
    vector<vector<int>> g(n, vector<int>(m));
    vector<int> bg;

    for (int i = 0; i < n; i ++ ) {
        for (int j = 0; j < m; j ++ ) cin >> g[i][j];
    }
    for (int ite = 0; ite < (1 << m); ite ++ ) {
        vector<int> tmp;
        for (int j = 0; j < m; j ++ ) if (ite >> j & 1) tmp.push_back(j);
        if ((int)tmp.size() != c) continue;
        bg.push_back(ite);
    }
    int num = (int)bg.size();
    vector<vector<LL>> sum(n, vector<LL>(num));
    vector<vector<vector<LL>>> con(n, vector<vector<LL>>(n, vector<LL>(num)));
    vector<vector<vector<LL>>> dp(n, vector<vector<LL>>(r + 1, vector<LL>(num, INF)));
    for (int i = 0; i < n; i ++ ) {
        for (int idx = 0; idx < num; idx ++ ) {
            int ite = bg[idx];
            vector<int> tmp;
            for (int j = 0; j < m; j ++ ) if (ite >> j & 1) tmp.push_back(j);
            for (int j = 0; j < c - 1; j ++ ) sum[i][idx] += abs(g[i][tmp[j + 1]] - g[i][tmp[j]]);
        }
    }
    for (int i = 0; i < n; i ++ ) {
        for (int j = i + 1; j < n; j ++ ) {
            for (int idx = 0; idx < num; idx ++ ) {
                int ite = bg[idx];
                for (int k = 0; k < m; k ++ ) {
                    if (ite >> k & 1) con[i][j][idx] += abs(g[j][k] - g[i][k]);
                }
            }
        }
    }
    for (int i = 0; i < n; i ++ ) {
        for (int j = 1; j <= r; j ++ ) {
            for (int idx = 0; idx < num; idx ++ ) {
                if (j == 1) dp[i][j][idx] = sum[i][idx];
                for (int v = 0; v < i; v ++ )
                    dp[i][j][idx] = min(dp[i][j][idx], dp[v][j - 1][idx] + sum[i][idx] + con[v][i][idx]);
            }
        }
    }

    LL ans = INF;
    for (int i = 0; i < n; i ++ ) {
        for (int idx = 0; idx < num; idx ++ ) {
            ans = min(ans, dp[i][r][idx]);
        }
    }
    cout << ans << endl;
}
signed main() {
#ifdef DEBUG
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    auto now = clock();
#endif
    ios::sync_with_stdio(false), cin.tie(nullptr);
    cout << fixed << setprecision(2);
    int _ = 1;
//    cin >> _;
    while (_ -- )
        solve();
#ifdef DEBUG
    cout << "============================" << endl;
    cout << "Program run for " << (clock() - now) / (double)CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif
    return 0;
}
posted @   Time_Limit_Exceeded  阅读(71)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 易语言 —— 开山篇
点击右上角即可分享
微信分享提示