poj 1050 解题报告 [动态规划] [最大子段和]

来源:互联网 发布:日亚海淘攻略 知乎 编辑:程序博客网 时间:2024/06/16 19:59



题目网址:

http://poj.org/problem?id=1050



【二维最大字段和DP】
先来复习一下最大字段和问题:
一维最大字段和:
设有数组a0,a1…an,找除其中连续的子段,使它们的和达到最大。
  if (tmpDP[i - 1] > 0)
   tmpDP[i] = tmpDP[i-1] + ar[i];
  else
   tmpDP[i] = ar[i];

  max = max > tmpDP[i] ? max : tmpDP[i];
  max即为最大字段和。
  
那么这个题目的关键其实就是,如何把二维的问题转换为一维。
这里要有一种思维方式:
猛一看我们这个题目是找子矩阵,矩阵的思考是非常复杂的。因为你每考虑多一行,就会因为增加的一行数据而导致计算更加复杂。如果在矩阵上硬算,相信一定是一个艰难的过程。
然而我们可以把矩阵的计算,转换为单行的情况。比如矩阵有3行,其实考虑所有情况,就是先计算矩阵各行的结果;然后再把相邻两行加起来,继续计算这种单行;最后再把3行加起来,计算单行。
这样我们就可以把矩阵里面所有的子矩阵全部遍历到了。
用DP的思想逐行往下处理就更快捷了,下一行可以用上一行的结果节省很多运算:



代码如下:
#include <stdio.h>

#define max(a,b) ((a)>(b)?(a):(b))
static int N;
static int array[101][101];
static int midValue[101][101][101];
static int dp[101];

static int GetMaxSubArrayValue(int ar[101]){
 int max = ar[1];
 int tmpDP[101];
 tmpDP[1] = ar[1];
 int i = 0;
 for (i = 2; i <= N;i++){
  if (tmpDP[i - 1] > 0)
   tmpDP[i] = tmpDP[i-1] + ar[i];
  else
   tmpDP[i] = ar[i];

  max = max > tmpDP[i] ? max : tmpDP[i];
 }
 return max;
}

static void CheckAllSubArray(int x){
 int k = 0;
 if (x == 1){
  for (k = 1; k <= N;k++){
   midValue[x][x][k] = array[x][k];
  }
  dp[x] = GetMaxSubArrayValue(midValue[x][x]);
  return;
 }
 dp[x] = dp[x - 1];
 int i = 0;
 for (i = x; i >= 1; i--){
  for (k = 1; k <= N; k++){
   midValue[x][i][k] = array[x][k] + midValue[x-1][i][k];
  }
  int tmp = GetMaxSubArrayValue(midValue[x][i]);
  dp[x] = dp[x] > tmp ? dp[x] : tmp;
 }
}
int main(){
 freopen("input.txt", "r", stdin);
 setbuf(stdout, NULL);
 scanf("%d", &N);

 int i = 0;
 int j = 0;


 for (i = 1; i <= N; i++){
  for (j = 1; j <= N; j++){
   scanf("%d", &array[i][j]);
   dp[i] = 0;
  }
 }
 for (i = 1; i <= N; i++){
  CheckAllSubArray(i);
 }

 printf("%d\n", dp[N]);
 return 0;
}

0 0