• P2331 [SCOI2005]最大子矩阵 (动规:分类讨论状态)


    题目链接:传送门

    题目:

    题目描述
    
    这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和最大。注意:选出的k个子矩阵不能相互重叠。
    输入输出格式
    输入格式:
    
    第一行为n,m,k(1≤n≤100,1≤m≤2,1≤k≤10),接下来n行描述矩阵每行中的每个元素的分值(每个元素的分值的绝对值不超过32767)。
    
    输出格式:
    
    只有一行为k个子矩阵分值之和最大为多少。
    
    输入输出样例
    输入样例#1: 复制
    
    3 2 2
    1 -3
    2 3
    -2 3
    
    输出样例#1: 复制
    
    9
    View Code

    PS:好像有很多类似的题目,上次的那道中国象棋(放炮的)也是,都是分类讨论多种情况就好了。

    思路:

    注意到m ≤ 2,所以可以按行dp,分类讨论转移的情况。

      ① 当前行不选;

      ② 当前行选左边;

      ③ 当前行选右边;

      ④ 当前行两个都选,但是作为两个矩阵;

      ⑤ 当前行两个都选,作为同一个矩阵;

    以上的m = 2时的情况,m = 1时更简单,只有选或者不选,就不赘述了。

    状态:

      f[i][j][k] 表示当前第i行,构造了j个矩阵,当前行的状态为k,(k = 0 - 4对应上面的① - ⑤)

    状态转移方程很容易得出,见代码。

    时间复杂度是O(NM4K)

    #include <bits/stdc++.h>
    
    using namespace std;
    const int INF = 0x3f3f3f3f;
    const int MAX_N = 100 + 5;
    const int MAX_M = 2 + 1;
    const int MAX_K = 15 + 1;
    
    inline void tomax(int&a, int b)
    {
        a = max(a, b);
    }
    
    int mat[MAX_N][MAX_M];
    int f[MAX_N][MAX_K][5];
    
    int main()
    {
        int N, M, K;
        cin >> N >> M >> K;
        for (int i = 1; i <= N; i++)
            for (int j = 1; j <= M; j++)
                scanf("%d", &mat[i][j]);
        memset(f, -INF, sizeof f);
        for (int i = 0; i <= N; i++)
            for (int j = 0; j <= K; j++)
                f[i][j][0] = 0;
        if (M == 1) {
            for (int i = 1; i <= N; i++)
                for (int j = 1; j <= K; j++) {
                    tomax(f[i][j][0], f[i-1][j][0]);
                    tomax(f[i][j][0], f[i-1][j][1]);
                    if (j-1 >= 0)
                        tomax(f[i][j][1], f[i-1][j-1][0]);
                    tomax(f[i][j][1], f[i-1][j][1]);
                    f[i][j][1] += mat[i][1];
                }
            cout << max(f[N][K][0], f[N][K][1]) << endl;
            return 0;
        }
        for (int i = 1; i <= N; i++) {
            for (int j = 1; j <= K; j++) {
                for (int k = 0; k <= 4; k++) {
                    //0:当前行不选
                    tomax(f[i][j][0], f[i-1][j][k]);
                    //1:当前行选左边
                    if ((k == 0 || k == 2 || k == 4) && (j-1 >= 0))
                        tomax(f[i][j][1], f[i-1][j-1][k]);
                    else if (k == 1 || k == 3)
                        tomax(f[i][j][1], f[i-1][j][k]);
                    //2:当前行选右边
                    if ((k <= 1 || k == 4) && j-1 >= 0)
                        tomax(f[i][j][2], f[i-1][j-1][k]);
                    else if (k == 2 || k == 3)
                        tomax(f[i][j][2], f[i-1][j][k]);
                    //3:当前行左右都选(分开)
                    if ((k == 0 || k == 4) && j-2 >= 0)
                        tomax(f[i][j][3], f[i-1][j-2][k]);
                    else if ((k == 1 || k == 2) && j-1 >= 0)
                        tomax(f[i][j][3], f[i-1][j-1][k]);
                    else if (k == 3)
                        tomax(f[i][j][3], f[i-1][j][k]);
                    //4:当前行左右都选(一起的)
                    if (k != 4 && j-1 >= 0)
                        tomax(f[i][j][4], f[i-1][j-1][k]);
                    else if (k == 4)
                        tomax(f[i][j][4], f[i-1][j][k]);
                }
                f[i][j][1] += mat[i][1];//1:当前行选左边
                f[i][j][2] += mat[i][2];//2:当前行选右边
                f[i][j][3] += mat[i][1] + mat[i][2];//3:当前行左右都选(分开)
                f[i][j][4] += mat[i][1] + mat[i][2];//4:当前行左右都选(一起的)
            }
        }
        int ans = -INF;
        for (int k = 0; k <= 4; k++)
            tomax(ans, f[N][K][k]);
        cout << ans;
        return 0;
    }
    /*
    3 1 2
    1
    -1
    2
    */
    View Code
  • 相关阅读:
    CSS 备忘
    header操作cookie
    定时器传参数
    Display 和Visible 区别
    php 笔记
    概要设计要求
    iOS 之 UITextView
    iOS 按钮设置图片和事件
    iOS 设置控件圆角、文字、字体
    iOS 之 UIScrollView
  • 原文地址:https://www.cnblogs.com/Lubixiaosi-Zhaocao/p/9877728.html
Copyright © 2020-2023  润新知