您的位置:首页 > 其它

bzoj[SCOI2005]最大子矩阵

2016-10-24 15:22 127 查看

描述

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

格式

输入格式

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

输出格式

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

样例

样例输入

3 2 2

1 -3

2 3

-2 3

样例输出

9

限制

每个测试点1s

来源

NOI2005四川省选拔赛第二试第4题

当时的省选题,放在现在难度已经不大了。如果这道题的 m 也是 1-100 ,那么就不好办了,好在它最大只有 2 = =。这个时候我们就可以分治处理,m=1 的时候怎么处理,m=2 的时候怎么处理,放在考场上这是个很实用的技巧,在想不出正解的时候分治处理特殊测试数据。

m=1 的情况应该很好想到,用 f[i][k] 表示这一列取到第 i 个数正好用了 k 个矩阵的最优解,就由几个状态转移过来:

1、不选第 i 个数。

2、选这个数构成一个新的矩阵,也就是从 k-1 到 i-1 中不取某个数。

方程:

for(int j=i-1;j>=k-1;j–)

f[i][k]=max(f[i][k],f[j][k-1]+sum[i][1]-sum[j][1]);

为什么是到 k-1 呢?这是因为状态是有选k-1个推过来,选的数肯定是大于等于构成的矩阵的数的。

m=1 的情况搞清楚了,那么m=2 的情况也就迎刃而解了。我们用f[i][j][k]表示第 1 列取了 i 个,第 2 列取了 j 个,正好用了 k 个矩阵,那么这个状态就要推两次上面的方程,分别是第一列的和第二列的。特殊情况,如果 i==j ,那么再推一次两列一起的就可以了。

代码如下:

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int map[110][20],f[1100][110][20];
int sum[110][20];
int main()
{
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>map[i][j];
sum[i][j]=sum[i-1][j]+map[i][j];
}
if(m==1)
{
f[1][1][1]=map[1][1];
for(int i=1;i<=n;i++)
for(int j=1;j<=k;j++)
{
f[i][1][j]=f[i-1][1][j];
for(int kk=i-1;kk>=j-1;kk--)
f[i][1][j]=max(f[i][1][j],f[kk][1][j-1]+sum[i][1]-sum[kk][1]);
}
cout<<f
[1][k];
return 0;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int kk=1;kk<=k;kk++)
{
f[i][j][kk]=max(f[i-1][j][kk],f[i][j-1][kk]);
for(int t=i-1;t>=kk-1;t--)
f[i][j][kk]=max(f[i][j][kk],f[t][j][kk-1]+sum[i][1]-sum[t][1]);
for(int t=j-1;t>=kk-1;t--)
f[i][j][kk]=max(f[i][j][kk],f[i][t][kk-1]+sum[j][2]-sum[t][2]);
if(i==j)
for(int t=i-1;t>=kk-1;t--)
f[i][j][kk]=max(f[i][j][kk],f[t][t][kk-1]+sum[i][1]-sum[t][1]+sum[j][2]-sum[t][2]);
}
cout<<f

[k];
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dp 省选