题解:P2258 [NOIP2014 普及组] 子矩阵
这道题一定要好好读题!我就被坑了
这道题是一道模拟+dp 的题目。
首先我们一个个枚举行选出了哪些行,接着我们 dp 列,令 \(f_{i,j}\) 为前 \(i\) 列选了 \(j\) 列,并且选了第 \(i\) 列,再令前面选的一个是 \(k\),那么转移是 \(f_{i,j} = \min(f_{k,j-1}+s1_{i}+s2_{k,i})\)。

那么这道题就能做出来啦!(细节详看代码)
AC code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 20;
const int inf = (1 << 29);
int n, m, r, c, ans = inf;
int a[N][N], row[N], s1[N], s2[N][N], dp[N][N];
int main() {
scanf("%d%d%d%d", &n, &m, &r, &c);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &a[i][j]);
for (int S = 0; S < (1 << n); S++) {
int cnt = 0;
for (int i = 0; i < n; i++)
if (S & (1 << i)) {
row[cnt] = i + 1;
cnt++;
}
if (cnt != r)continue;
for (int i = 1; i <= m; i++) {
s1[i] = 0;
for (int j = 0; j < cnt - 1; j++)
s1[i] += abs(a[row[j]][i] - a[row[j + 1]][i]);
for (int k = i + 1; k <= m; k++) {
s2[i][k] = 0;
for (int j = 0; j < cnt; j++)
s2[i][k] += abs(a[row[j]][i] - a[row[j]][k]);
}
}
for (int i = 0; i <= m; i++)for (int j = 0; j <= c; j++)
dp[i][j] = inf;
dp[0][0] = 0;
for (int i = 1; i <= m; i++) {
dp[i][1] = s1[i];
for(int j = 2; j <= c;j++)
for (int k = 1; k < i; k++)
dp[i][j] = min(dp[i][j], dp[k][j - 1] + s1[i] + s2[k][i]);
ans = min(ans,dp[i][c]);
}
}
printf("%d",ans);
}
完结撒花!

浙公网安备 33010602011771号