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;
}