【题解】 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();}
```

原文地址:https://www.cnblogs.com/NHDR233/p/11246712.html

时间: 2024-10-14 06:06:10

【题解】 P2258 子矩阵的相关文章

P2258 子矩阵——搜索+dp

P2258 子矩阵 二进制枚举套二进制枚举能过多一半的点: 我们只需要优化一下第二个二进制枚举的部分: 首先我们先枚举选哪几行,再预处理我们需要的差值,上下,左右: sum_shang,sum_heng 然后DP查找最小值 dp[i][j]表示前i列已经选了j列: #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=20; int n,m,r

P2258 子矩阵

题目描述 给出如下定义: 子矩阵:从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与列的相对顺序)被称为原矩阵的一个子矩阵. 例如,下面左图中选取第2.4行和第2.4.5列交叉位置的元素得到一个2*3的子矩阵如右图所示. 9 3 3 3 9 9 4 8 7 4 1 7 4 6 6 6 8 5 6 9 7 4 5 6 1 的其中一个2*3的子矩阵是 4 7 4 8 6 9 相邻的元素:矩阵中的某个元素与其上下左右四个元素(如果存在的话)是相邻的. 矩阵的分值:矩阵中每一对相邻元素之差

luogu P2258 子矩阵 |动态规划

题目描述 给出如下定义: 子矩阵:从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与列的相对顺序)被称为原矩阵的一个子矩阵. 例如,下面左图中选取第22.44行和第22.44.55列交叉位置的元素得到一个2 \times 32×3的子矩阵如右图所示. 9 3 3 3 9 9 4 8 7 4 1 7 4 6 6 6 8 5 6 9 7 4 5 6 1 的其中一个2 \times 32×3的子矩阵是 4 7 4 8 6 9 相邻的元素:矩阵中的某个元素与其上下左右四个元素(如果存在的话

【Luogu】P2258子矩阵(状态压缩,DP)

233今天蒟蒻我连文化课都没听光想着这个了 然后我调了一下午终于过了!!! 一看数据范围似乎是状压,然而216等于65536.开一个65536*65536的二维数组似乎不太现实. 所以Rqy在四月还是几月给我们讲这道题的时候说要半DFS半DP,时间复杂度O(2n*n3) 怎么个半DFS半DP法呢? 其实我没DFS.所以这个问题不重要. 我真的没用DFS.枚举从1到2n-1的所有集合,把二进制数中1的个数不等于r的都筛掉.然后对于每个状态,预处理出每一列内部的代价,预处理出列与列之间的代价,然后进

Hackerrank: Week of Code 36

Cut a Strip 题目简述:给定$n \times m$的矩阵$a[][]$,要求选择一个$x \times 1(1 \leq x \leq k)$的(连续)子矩阵并清零后,找到最大和的(连续)子矩阵. 数据范围:$1 \leq n, m, k \leq 380$. 题解: 子矩阵可以用四个参数表示$(x_1, y_1, x_2, y_2)$,其中$(x_1, y_1)$是其左上角,$(x_2, y_2)$是其右上角. 我们枚举子矩阵的$y_1$和$y_2(1 \leq y_1 \leq

[DP专题]悬线法

reference:浅谈用极大化思想解决最大子矩阵问题 两种思路: 1.思想一:枚举所有的极大有效子矩形,如奶牛浴场 2.思想二:垂线法(后文介绍) 题目来源: [最大全0子正方形]p1387 最大正方形 P1169 棋盘制作 [最大全0子正方形]p2701 巨大的牛棚 [最大子矩阵的和(1e3数量级)]p4147 玉蟾宫 P1578 奶牛浴场 本题坐标范围在3e4,所以无法dp,爆内存 reference [缺陷:可拓展性不够(后文例题介绍),在点(最多N * M)密集的情况下表现较差] #d

NOI题库 1768最大子矩阵 题解

NOI题库 1768最大子矩阵  题解 总时间限制: 1000ms 内存限制: 65536kB 描述 已知矩阵的大小定义为矩阵中所有元素的和.给定一个矩阵,你的任务是找到最大的非空(大小至少是1 * 1)子矩阵. 比如,如下4 * 4的矩阵 0 -2 -7 0 9 2 -6 2 -4 1 -4 1 -1 8 0 -2 的最大子矩阵是 9 2 -4 1 -1 8 这个子矩阵的大小是15. 输入   输入是一个N * N的矩阵.输入的第一行给出N (0 < N <= 100).再后面的若干行中,依

题解+新技巧--一本通1282:最大子矩阵

http://ybt.ssoier.cn:8088/problem_show.php?pid=1282(题目传送) 虽然已知是DP,但第一眼看挺蒙的,想了想后设了个a[i][j][k][l]表示长(坐标)为i~j,宽(坐标)为k~l的矩阵,但根本找不到状态转移方程啊.后借鉴题解(https://www.cnblogs.com/GodA/p/5237061.html)后领悟到的另一种方法: 任何问题都有它的简化,看到二维,没办法时我们可以考虑一下一维:求一维数组的一个最大连续段,我们可以设b[i]

洛谷 U360 子矩阵 (NOIP模拟赛T1)题解

题目链接:https://www.luogu.org/problem/show?pid=U360 题目背景 夏令营 题目描述 小A有一个N×M的矩阵,矩阵中1~N*M这(N*M)个整数均出现过一次.现在小A在这个矩阵内选择一个子矩阵,其权值等于这个子矩阵中的所有数的最小值.小A想知道,如果他选择的子矩阵的权值为i(1<=i<=N×M),那么他选择的子矩阵可能有多少种?小A希望知道所有可能的i值对应的结果,但是这些结果太多了,他算不了,因此他向你求助. 输入输出格式 输入格式: 第一行,两个整数