题解: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);
}

完结撒花!

posted @ 2026-01-02 13:37  OI_emperor  阅读(2)  评论(0)    收藏  举报