【每日一题】[SCOI2005]最大子矩阵

[SCOI2005]最大子矩阵

https://ac.nowcoder.com/acm/problem/20242


题目

题目描述:
这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和最大。
注意:选出的k个子矩阵 不能相互重叠。

输入描述:
第一行为n,m,k(1 ≤ n ≤ 100,1 ≤ m ≤ 2,1 ≤ k ≤ 10),
接下来n行描述矩阵每行中的每个元素的分值(每个元素的分值的绝对值不超过32767)。

输出描述:
只有一行为k个子矩阵分值之和最大为多少。


解析

1)知识点

  • 这是一道动态规划的题目。

2)看题目

  1. 首先看题目,就是让我们在矩阵里面找k个不重叠矩阵让他们加起来最大
  2. 题目给的条件就很奇妙,m最大才2,n最大也才1e2,说明我们开个三维dp数组或者来个三重循环都不是问题。

3)算法分析

  1. 这道题我们分析一下,其实也就是一道多种情况判断的dp罢了。
  2. 说到dp,那就是:动态规划最重要的就是递推和dp数组的含义
  3. 首先dp数组的含义就涉及到了我们这道题dp的中心思想:
    1. 我们一维的时候就是一个线性的dp+前缀和求区间值
    2. 然后这道题,我们一个m = 2的情况,也就是两条线罢了,单纯的组合起来就好了。
    3. 所以我们可以建立一个三维的dp:dp[第一列位置][第二列的位置][选取k个区块] = 最大区块和
  4. 然后就是递推了呢:
    1. 递推也就是这么几个点,首先两条线性的时候就可以单纯的线性判断。
    2. 如果两边判断的位置相等的时候呢,我们就可以判断一下同时取两边的情况了。
  5. 说了这么多,我还没讲到线性的怎么写:
    1. 我们就是简单的不好的就用区间代替就好了:
      for (int l = 0; l < i; l++)
              dp[i][k] = max(dp[i][k], dp[l][k - 1] + sum[i] - sum[l]);
      //i为数组位置,k为选取区间数量
  6. 就是如此。

4)算法操作

  1. 操作主要就是讲一下怎么递推了嘛。
  2. 其实就是枚举一下第一列,枚举一下第二列,再枚举一下选取空间而已。十分暴力的dp。
  3. 所以我们先做一个暴力三重循环:
    for (int i = 1; i <= n; i++)//枚举第一列
        for (int j = 1; j <= n; j++)//枚举第二列
            for (int k = 1; k <= num; k++)//枚举选取次数
    
  4. 然后最第一列第二列,还有一二列同时选的情况做个更新:
    dp[i][j][k] = max(dp[i - 1][j][k], dp[i][j - 1][k]);
    for (int l = 0; l < i; l++)
        dp[i][j][k] = max(dp[i][j][k], dp[l][j][k - 1] + sum[i][0] - sum[l][0]);
    for (int l = 0; l < j; l++)
        dp[i][j][k] = max(dp[i][j][k], dp[i][l][k - 1] + sum[j][1] - sum[l][1]);
    if (i == j)
        for (int l = 0; l < i; l++) {
                int sm0 = sum[i][0] - sum[l][0];
            int sm1 = sum[j][1] - sum[l][1];
            dp[i][j][k] = max(dp[i][j][k], dp[l][l][k - 1] + sm0 + sm1);
        }
    
  5. 最后输出最后一个位置的就好了:
    cout << dp[n][n][num] << endl;

5)打代码

  1. 首先输入数据。
  2. 然后就阔以开始暴力dp啦。
  3. 下面全代码~


AC代码

#include <iostream>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//代码预处理区

const int MAX = 1e2 + 7;
int n, m, num;
int sum[MAX][2], dp[MAX][MAX][17];
//全局变量区

int main() {
    IOS;
    cin >> n >> m >> num;
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < m; j++) {
            cin >> sum[i][j];
            sum[i][j] += sum[i - 1][j];
        }
    for (int i = 1; i <= n; i++)//枚举第一列
        for (int j = 1; j <= n; j++)//枚举第二列
            for (int k = 1; k <= num; k++) {//枚举选取次数
                dp[i][j][k] = max(dp[i - 1][j][k], dp[i][j - 1][k]);
                for (int l = 0; l < i; l++)
                    dp[i][j][k] = max(dp[i][j][k], dp[l][j][k - 1] + sum[i][0] - sum[l][0]);
                for (int l = 0; l < j; l++)
                    dp[i][j][k] = max(dp[i][j][k], dp[i][l][k - 1] + sum[j][1] - sum[l][1]);
                if (i == j)
                    for (int l = 0; l < i; l++) {
                        int sm0 = sum[i][0] - sum[l][0];
                        int sm1 = sum[j][1] - sum[l][1];
                        dp[i][j][k] = max(dp[i][j][k], dp[l][l][k - 1] + sm0 + sm1);
                    }
            }
    cout << dp[n][n][num] << endl;
    return 0;
}
//函数区
每日一题 文章被收录于专栏

憨憨的专栏

全部评论

相关推荐

01-12 14:08
门头沟学院 Java
有寒假来武汉小米总部实习的大学生嘛,我也是小米的员工,想找合租舍友,仅限女生可免租半月,二月初可入住,也就是说房租是2.15开始算的哦~也可以将行李提前放过来~房屋介绍:1、房子情况:有电梯;租的是三室一厅一卫一厨,&nbsp;但是有个卧室比较小,不打算找人,只住两个人就可以了;衣柜也很大,可以放下很多衣服;房屋采光真的很好,早上起来可以在床上晒太阳的那种,十分惬意(夏季晚上十分好看!)2.&nbsp;楼下离我们很近的地方有小吃街和一个两层大超市(大概步行两分钟多就可以走到)&nbsp;,还有一个新开的麦当劳,晚上可以去吃小吃,购买物资也可以去大超市;3.&nbsp;房子基本设施齐备(洗衣机,冰箱,空调,油烟机,热水器);4.&nbsp;我有稳定的工作,生活中很注意卫生,周末有时间会自己做饭,可以投喂哦~5.&nbsp;出行:距离公交站步行10分钟不到,距政务中心,武汉小米总部三站(晚上我都是走回来的,很近的~);一个比较进的地铁,距离大概1km左右;出入我觉得很方便;6.&nbsp;房租:1150每月,押一付二,无物业费,也没有中介费和其他额外费用。7.&nbsp;民用水电燃气,用多少交多少,水电费正常平摊。希望你是:1.&nbsp;女生(本人女),不带异性回家,如有同性朋友来玩,最多过夜一晚;2.&nbsp;爱干净,讲卫生,作息正常,不吵闹,有稳定工作;3.&nbsp;好沟通,有任何问题一定要沟通,不要闷着!中介勿扰,非诚勿扰!!!希望不要浪费彼此的时间诚心有意向的可以联系我看房
租房找室友
点赞 评论 收藏
分享
2025-12-15 14:25
云南大学 Java
lei22:入职可能会看学信网,最好别伪装,这个简历找实习肯定是够的,肯定会有收 28 届实习生的公司的,多投就行
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务