题解 P2258 子矩阵
Posted nhdr233
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 P2258 子矩阵相关的知识,希望对你有一定的参考价值。
在矩阵中求解最优情况,其中$n \leq 16$
假设现在已经得到保留的行与列的编号,递推矩阵分值的复杂度是$O(n^2)$遍历一遍就ok
50pts 假设行列全部枚举全排列的话,枚举次数在最坏情况下是$(C_12^6)^2=924^2$,在加上求分值的复杂度则总复杂度还是勉强可以接受的?
直接看100pts解法,仍然枚举一次行的全排列,复杂度是$O(C_m^c)$,这个时候我们得到的其实是一行数列,数列上的每个数有选择和不选择两种状态,选择每个数会增加分值,分别是:
- 自身的分数(该列上下数字差值绝对值和)
- 相对左侧(右侧)的分数,左侧和右侧的选择都会影响这种相对分数
那么提前预处理出数列上每个数两两之间相对的分数和自身的分数,因为要枚举$m$的原因,所以复杂度是$O(n^3)$
进行dp,设$f[i][l]$为已选择到数列上第$i$个数,已选择$l$个数的最大值,很明显$f[i][l]=min(f[k][l-1]+sum[k][i])$ 其中$k \subseteq[1,i-1]$,不需要太多优化,只要枚举$i,k,l$就可以在$O(n^3)$之内解决问题,该种方法总复杂度$O(C_m^c n^3)$可以通过$n=16$的数据
```
//2019/7/19->Riko->AtNCU->luoguP2258
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
bool digit (int ch) return (ch <= ‘9‘ and ch >= ‘0‘);
inline int in ()
int x = 0, ch = getchar(), base = 1;
while (!digit(ch))
if (base == ‘-‘) base = -1;
ch = getchar();
while (digit(ch)) x = x*10+ch-‘0‘, ch = getchar();
return x*base;
template <typename T> inline void smin (T& x, T y) if (x > y) x = y;
inline int abs (int x) return (x < 0) ? -x : x;
const int N = 24;
int n, m, r, c, ans, cnt;
int f[N][N], a[N][N], sum[N][N], suml[N], bol[N];
void work ()
memset(sum, 0, sizeof(sum));
memset(suml, 0, sizeof(suml));
memset(f, 0x3f, sizeof(f));
for (int i = 1; i <= n; ++i)
for (int j = i+1; j <= n; ++j)
int seg = 0;
for (int l = 1; l <= m; ++l)
if (bol[l]) seg += abs(a[i][l]-a[j][l]);
sum[i][j] = sum[j][i] = seg;
int last = 0;
for (int l = 1; l <= m; ++l)
if (bol[l])
if (last) suml[i] += abs(a[i][l]-last);
last = a[i][l];
for (int i = 1; i <= n; ++i) f[i][1] = suml[i];
for (int i = 1; i <= n; ++i)
for (int l = 2; l <= r; ++l)
int Min = f[0][0];
for (int j = 1; j < i; ++j)
smin(Min, f[j][l-1]+sum[j][i]);
f[i][l] = Min+suml[i];
for (int i = 1; i <= n; ++i) smin(ans, f[i][r]);
void dfs (int idx, int num)
if (num == c)
work();
return;
for (int i = idx; i <= m; ++i)
bol[i] = true;
dfs(i+1, num+1);
bol[i] = false;
void prepare ()
n = in(); m = in();
r = in(); c = in();
ans = 999999999;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
a[i][j] = in();
dfs(1, 0);
printf("%d", ans);
int main () prepare();
```
以上是关于题解 P2258 子矩阵的主要内容,如果未能解决你的问题,请参考以下文章