[NOIP2014 普及组] 子矩阵 题解
[NOIP2014 普及组] 子矩阵
解法
暴力
先介绍比较暴力的解法,不难想出,我们可以直接暴力搜索每个子矩阵。对于每个子矩阵的贡献,我们求出最小的那个即可。
复杂度为
大概能得 60
分。
优化
考虑对暴力代码进行优化:注意到行与列的贡献是可以分开算的,二者之间并不会互相影响。所以我们可以先预处理出 和 数组,对于前者,表示对第 行,选择列状态为 时候的行贡献,后者同理。
复杂度为
大概能得 80
分。
正解
对于暴力的优化我自己已经想不出更好的了,所以考虑其他写法。
考虑本题的一维状态下的情况,即对于一个数组 来说,取 个值,问取出来相邻数之间的差的绝对值之和最小。对于这个问题,很容易想到用 的方法去做。我们定义 表示前 个数选了 个(其中 是选了的)的最小贡献。转移为
注意到在本题中行与列之间的贡献不会相互影响,所以我们可以把一行(列)的状态看作一个整体后,转化为一维状态下的问题进行考虑。
令 表示前 行, 选了 个,选的列状态为 的情况下的最小贡献。那么我们转移为
其中 表示第 行选择列的状态为 时的行贡献, 表示第 行和第 行,选择的状态为 的对应列元素之间的差绝对值。
复杂度为
代码
#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;
}