P2331 [SCOI2005]最大子矩阵

$ \color{#0066ff}{ 题目描述 }$

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

\(\color{#0066ff}{输入格式}\)

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

\(\color{#0066ff}{输出格式}\)

只有一行为k个子矩阵分值之和最大为多少。

\(\color{#0066ff}{输入样例}\)

3 2 2
1 -3
2 3
-2 3

\(\color{#0066ff}{输出样例}\)

9

\(\color{#0066ff}{数据范围与提示}\)

none

\(\color{#0066ff}{题解}\)

蒟蒻太弱了。。蒟蒻什么都不会。。只会乱搞。。

一看\(m\le2\),显然乱搞啊

Part one m=1

这部分很好写把,直接\(O(n^2k)\)DP就行了

\(f[i][k]\)表示前i个数,分成k段的最大值,可以预处理一个区间最大子段和转移就行

具体可以看代码

Part two m=2

我写的可以说是及其恶心。。

一下把两列理解为两个序列a,b

刚开始,我只考虑到了3种情况,因为子矩阵只会有三种形态,a中的一段,b中的一段,a和b合一起大的一段

然后可以预处理出a序列,b序列,ab合一起的序列的区间最大子段和,然后类似于Part One转移

然而,情况并没有考虑全,但是可以拿到70pts

还有什么情况呢??

比如,我在a中选了\([4,6]\)这一段,b中选了\([5,7]\)这一段,然而DP中并不会允许两个同时选,因为这样DP会让其误以为重叠!!

所以,蒟蒻想到一个办法,在设一个数组\(p[i][j][k]\)代表从\([i,j]\)选k段的答案

这样转移的时候用p就方便多了

p怎么预处理呢?? 其实就是n遍Part one

然而。。。。90pts!!!

蒟蒻冥思苦想,大力观察,发现居然还有一种可能!!

比如在b中选了一个长度贼大的区间,在a中选了一大堆长度很小的区间,b这个大区间把这些小区间全部包含了,这种情况并没有转移到p!!

蒟蒻再次冥思苦想,又开了两个数组\(ff[i][j][k]\)表示在a中从i到j选k段的答案
gg是在b中的,含义同ff

这个还是类型Part one的DP,可以预处理

更新\(p[sta][i][l]\)的时候,可以用\(p[sta][j - 1][l - num] + ff[j][i][r] + gg[j][i][num - r]\)

然后终于把p弄完了,DP就行了。。

复杂度O(\(n^3k^3\times\)极小常数)

#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
const int inf = 0x3f3f3f3f;
const int maxn = 150;
int n, m, k;
namespace partone {
    int f[maxn][15], d[maxn][maxn], a[maxn];
    void predoit() {
        for(int i = 1; i <= n; i++) a[i] = in();
        for(int i = 1; i <= n; i++) {
            int ans = -inf, now = 0;
            for(int j = i; j <= n; j++) {
                if(now + a[j] < 0) now = 0;
                else now += a[j];
                ans = std::max(ans, now);
                d[i][j] = ans;
            }
        }
    }
    void DP() {
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= k; j++) f[i][j] = -inf;
            for(int j = 1; j <= i; j++)
                for(int l = 1; l <= k; l++)
                    f[i][l] = std::max(f[i][l], f[j - 1][l - 1] + d[j][i]);
        }
        printf("%d\n", f[n][k]);
    }
    int main() {
        predoit();
        DP();
        return 233;
    }
}
namespace parttwo {
 	int d[maxn][maxn][3], f[maxn][15], a[maxn], b[maxn], p[maxn][maxn][15];
 	int ff[maxn][maxn][15], gg[maxn][maxn][15];
 	void predoit() {
        for(int i = 1; i <= n; i++) a[i] = in(), b[i] = in();
        for(int i = 1; i <= n; i++) {
            int ans = -inf, now = 0;
            for(int j = i; j <= n; j++) {
                if(now + a[j] < 0) now = 0;
                else now += a[j];
                ans = std::max(ans, now);
                d[i][j][1] = ans;
            }
        }
        for(int i = 1; i <= n; i++) {
            int ans = -inf, now = 0;
            for(int j = i; j <= n; j++) {
                if(now + b[j] < 0) now = 0;
                else now += b[j];
                ans = std::max(ans, now);
                d[i][j][2] = ans;
            }
        }
        for(int i = 1; i <= n; i++) {
            int ans = -inf, now = 0;
            for(int j = i; j <= n; j++) {
                if(now + a[j] + b[j] < 0) now = 0;
                else now += a[j] + b[j];
                ans = std::max(ans, now);
                d[i][j][0] = ans;
            }
        }
        for(int sta = 1; sta <= n; sta++) {
            for(int i = sta; i <= n; i++) {
                for(int j = 1; j <= k; j++) ff[sta][i][j] = gg[sta][i][j] = -inf;
                for(int j = sta; j <= i; j++)
                    for(int l = 1; l <= k; l++) {
                        ff[sta][i][l] = std::max(ff[sta][i][l], ff[sta][j - 1][l - 1] + d[j][i][1]);
                        gg[sta][i][l] = std::max(gg[sta][i][l], gg[sta][j - 1][l - 1] + d[j][i][2]);
                    }
                }
        }
        for(int sta = 1; sta <= n; sta++) {
            for(int i = sta; i <= n; i++) {
                for(int j = 1; j <= k; j++) p[sta][i][j] = -inf;
                for(int j = sta; j <= i; j++)
                    for(int l = 1; l <= k; l++) {
                        p[sta][i][l] = std::max(p[sta][i][l], p[sta][j - 1][l - 1] + d[j][i][1]);
                        p[sta][i][l] = std::max(p[sta][i][l], p[sta][j - 1][l - 1] + d[j][i][2]);
                        for(int num = 0; num <= l; num++)
                            for(int r = 0; r <= num; r++)
                                p[sta][i][l] = std::max(p[sta][i][l], p[sta][j - 1][l - num] + ff[j][i][num - r] + gg[j][i][r]);
                    }
            }
        }
    }
    void DP() {
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= k; j++) f[i][j] = -inf;
            for(int j = 1; j <= i; j++)
                for(int l = 1; l <= k; l++) {
                    f[i][l] = std::max(f[i][l], f[j - 1][l - 1] + d[j][i][0]);
                    for(int num = 0; num <= l; num++)
                        f[i][l] = std::max(f[i][l], f[j - 1][l - num] + p[j][i][num]);
                }
        }
        printf("%d\n", f[n][k]);
    }
 	int main() {
 		predoit();
 		DP();
 		return 520;
 	}
}
int main() {
    n = in(), m = in(), k = in();
    if(m == 1) partone::main();
    else parttwo::main();
    return 0;
}
posted @ 2019-03-28 20:42  olinr  阅读(269)  评论(1编辑  收藏  举报