码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj1084: [SCOI2005]最大子矩阵(DP)

时间:2019-01-01 18:50:39      阅读:179      评论:0      收藏:0      [点我收藏+]

标签:格式   重叠   names   ==   输出   多少   ons   com   while   

原题链接

题目描述:这里有一个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

解析:注意1≤m≤2!
???当m=1时,很容易想到可以用DP解决。
???设dp[i][j]表示前i个数,取了j个矩阵的最大值。
???如果不选,dp[i][j]=max(dp[i][j],dp[i-1][j)。
???如果选,dp[i][j]=max(dp[i][j],dp[l][j-1]+sum[i]-sum[l])。
???当m=2时,也考虑用DP进行解决。
???设f[i][j][k]表示第一列选到了第i个,第二列选到了第j个,一共选了k个矩阵的最大值。
???那么有以下几种情况。
???若不选,f[i][j][k]=max(f[i-1][j][k],f[i][j-1][k])。
???若只选第一列,f[i][j][k]=max(f[i][j][k],f[l][j][k-1]+sum[i][0]-sum[l][0])。
???若只选第二列,f[i][j][k]=max(f[i][j][k],f[i][l][k-1]+sum[j][1]-sum[l][1])。
???当i=j时,可以两行都选。
???同时可以两行当成一个矩阵选,也可以当成两个矩阵选。
???若当成一个矩阵,f[i][j][k]=max(f[i][j][k],f[l][l][k-1]+sum[i][0]-sum[l][0]+sum[j][1]-sum[l][1])。
???若当成两个矩阵,f[i][j][k]=max(f[i][j][k],f[l][l][k-2]+sum[i][0]-sum[l][0]+sum[j][1]-sum[l][1])。

代码如下:

#include<cstdio>
#include<algorithm>
using namespace std;

const int maxn = 105;
int n, m, k, a[maxn][maxn], dp[maxn][maxn], sum[maxn][2];
int f[maxn][maxn][maxn];

int read(void) {
    char c; while (c = getchar(), (c < '0' || c > '9') && c != '-'); int x = 0, y = 1;
    if (c == '-') y = -1; else x = c - '0';
    while (c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; return x * y; 
}

void subtask1(void) {
    for (int i = 1; i <= n; ++ i) sum[i][0] = sum[i - 1][0] + a[i][1];
    for (int i = 1; i <= n; ++ i)
      for (int j = 1; j <= k; ++ j) 
        for (int h = i - 1; h >= 0 ; -- h) {
          dp[i][j] = max(dp[i][j], dp[i - 1][j]);
          dp[i][j] = max(dp[i][j], dp[h][j - 1] + sum[i][0] - sum[h][0]);
        }
    printf("%d", dp[n][k]);
}

void subtask2(void) {
    for (int i = 1; i <= n; ++ i) {
      sum[i][0] = sum[i - 1][0] + a[i][1];
      sum[i][1] = sum[i - 1][1] + a[i][2];
    }
    for (int i = 1; i <= n; ++ i) 
      for (int j = 1; j <= n; ++ j)
        for (int h = 1; h <= k; ++ h) {
          f[i][j][h] = max(f[i - 1][j][h], f[i][j - 1][h]);
          for (int l = i - 1; l >= 0; -- l) f[i][j][h] = max(f[i][j][h], f[l][j][h - 1] + sum[i][0] - sum[l][0]);
          for (int l = j - 1; l >= 0; -- l) f[i][j][h] = max(f[i][j][h], f[i][l][h - 1] + sum[j][1] - sum[l][1]);
          if (i == j) {
            for (int l = i - 1; l >= 0; -- l) {
                f[i][j][h] = max(f[i][j][h], f[l][l][h - 1] + sum[i][0] - sum[l][0] + sum[j][1] - sum[l][1]);
                if (h > 1) f[i][j][h] = max(f[i][j][h], f[l][l][h - 2] + sum[i][0] - sum[l][0] + sum[j][1] - sum[l][1]);
              }
          }
        }
    printf("%d", f[n][n][k]);
}

int main() {
    n = read(); m = read(); k = read();
      for (int i = 1; i <= n; ++ i)
        for (int j = 1; j <= m; ++ j) a[i][j] = read();
      if (m == 1) subtask1();
      else subtask2();
    return 0;
} 

bzoj1084: [SCOI2005]最大子矩阵(DP)

标签:格式   重叠   names   ==   输出   多少   ons   com   while   

原文地址:https://www.cnblogs.com/Gaxc/p/10205442.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!