题解 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 子矩阵的主要内容,如果未能解决你的问题,请参考以下文章

洛谷P2258 子矩阵

P2258 子矩阵

luogu P2258 子矩阵 |动态规划

NOIP 普及组 T4 子矩阵(--洛谷P2258)

P2258 子矩阵——搜索+dp

P2258 子矩阵