1. 程式人生 > >poj 1050-小白演算法練習 to the max 動態規劃

poj 1050-小白演算法練習 to the max 動態規劃

To the Max
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 49226 Accepted: 26074

Description

Given a two-dimensional array of positive and negative integers, a sub-rectangle is any contiguous sub-array of size 1*1 or greater located within the whole array. The sum of a rectangle is the sum of all the elements in that rectangle. In this problem the sub-rectangle with the largest sum is referred to as the maximal sub-rectangle. 
As an example, the maximal sub-rectangle of the array: 

0 -2 -7 0 
9 2 -6 2 
-4 1 -4 1 
-1 8 0 -2 
is in the lower left corner: 

9 2 
-4 1 
-1 8 
and has a sum of 15. 

Input

The input consists of an N * N array of integers. The input begins with a single positive integer N on a line by itself, indicating the size of the square two-dimensional array. This is followed by N^2 integers separated by whitespace (spaces and newlines). These are the N^2 integers of the array, presented in row-major order. That is, all numbers in the first row, left to right, then all numbers in the second row, left to right, etc. N may be as large as 100. The numbers in the array will be in the range [-127,127].

Output

Output the sum of the maximal sub-rectangle.

Sample Input

4
0 -2 -7 0 9 2 -6 2
-4 1 -4  1 -1

8  0 -2

Sample Output

15

翻譯

給出一個有正整數和負整數的二維的向量,附屬矩陣是在向量中數個矩形就是其附屬矩形,(>1*1),附屬矩形的和是各個元素的和

IN

第一行:N代表這個N*N矩陣的維數

接下來幾行輸入16個整數,以空白符相隔無論是space還是tab還是enter

OUT

第一行(最有一行):附屬矩形和的最大數 

第一行:N代表這個N*N矩陣的維數

分析:

在做這道題之前必須會最大欄位和這一道題的基礎,這也是動態規劃中最簡單的一道例題,而這道題是最大欄位和的一個擴充套件。

例子: 0,2,7,2,-5最大的一個連續子序列之和。

分析:這五個數有這樣幾種和的組合:0;02;027;0272;……這樣5!個數。

   用陣列索引來表示就是:0;0到1;0到2;0到3……1;1到2;……(這裡是陣列索引表示)

   我們顯然不能用多重迴圈把所有數都算出來,我們換一種角度思考,如果只有一個數字,那麼最大的連續子序列數多少,還是以上面的例子為例,應該是0吧,那麼現在有兩個數字呢有兩種情況,一種是前面有一個數字的最大值(已求)+現在這個數字;另外一種情況是就是隻有這個數字然後算出來的最大值與只有一個數字的情況的最大值相比較;現在有三個數字也有兩種情況,一種是前面有前面兩個數字的最大值+現在這個數字;或者就是隻是這個數字;……

程式碼:

第一行:N代表這個N*N矩陣的維數

int max_sum(int n)
{
         int i, sum = 0, max = INT_MIN;  
         for(i = 0; i < n; i++)
         {  -------------------------------------------------------------------------------0
                if(sum < 0)
                        sum = 0;
                sum += a[i];      ---------------------------------------------------------1
                if(sum > max)
                        max = sum;---------------------------------------------------------2
         }
         return max;
}

哭上面把文字變成程式碼就是我們普通程式設計師做的事情,文字表述部分就是大神們做的事情。哭

算了,不哭了,我來解釋一下為什麼文字翻譯成了一樣一些程式碼。

----0----代表藍色部分【這個不用說迴圈】

----1----代表橙色部分【**下面說**】

----2----代表紫色部分

我們橙色的字;因為一共有兩種情況,一種是自己本身,一種是前i-1項的最大值,我們換個思路想一下,假如第1個數(索引為0的數)為負數,那麼下面前兩個數的最大最一定是第二個數其本身,即第二次是把sum=0;sum+=a[1]  然後記錄max值。。。實際上就是假如你前n個數是正的那麼可以繼續加上去,如果你前n個數是負的那麼把值記錄一下就好了。所以只要求前n項和和最大值就好了。

當然也有人是這樣寫的:

  1. int MaxSum(int n,int *a)  
  2. {  
  3.     int sum=0,b=0;  
  4.     for(int i=1; i<=n; i++)  
  5.     {  
  6.         if(b>0)  
  7.         {  
  8.             b+=a[i];  
  9.         }  
  10.         else
  11.         {  
  12.             b=a[i];  
  13.         }  
  14.         if(b>sum)  
  15.         {  
  16.             sum = b;  
  17.         }  
  18.     }  
  19.     return sum;  

或者更加直白的話是這樣寫的,這樣更加容易理解一些,看個人的程式設計習慣了

int max_sum2(int n)
{
     b[0]=a[0];    //陣列b存放前n項的最大值
     int maximal=b[0];
     for(int i=1;i<n;i++)
     {
           b[i]=max(b[i-1]+a[i],a[i]); //STL
           if(b[i]>maximal)
                maximal=b[i];
     }
     return maximal;
}
下面寫二維的矩陣這道題,我們首先要做的就是降維然後像上面一樣做。

程式碼:

先寫一下思路,為什麼說是最大欄位和的擴充套件。。。
我們原來是一行,如果算到兩行的話。。。現在我們下面一行跟著上面一行一起動,再求和。即如果這個矩陣只有兩行,我們先求第一行的最大值(最大欄位和),然後讓第二行跟著第一行一起動【其實就是把兩行看成一行】,求出兩行的最大值。。。以此類推。 我們知道一維時候的最大欄位和,要有sum【前n個數之和】和最大值這兩個變數,我們這裡的sum怎麼求呢? sum[i][j]從表示第i行第j個元素變成表示第i行前j個元素和,這樣sum[k][j]-sum[k][i]就可以表示第k行從i->j列的元素和 程式碼:
#include<iostream>
#include<algorithm>
using namespace std;
const int MAX=101;
int arr[MAX][MAX]={0};
int Dy[MAX][MAX]={0};
int main(){
	int N;
	cin>>N;
	for(int i=1;i<=N;i++)
	{
		for(int j=1;j<=N;j++)
		{
			cin>>arr[i][j];
		 	Dy[i][j]=Dy[i][j-1]+arr[i][j]; //讓Dy存放第i行的前j項和
			//其實我們用數學之美來看為什麼要這樣存放,你一維的時候存放的是一維的資料
			//二維的時候再存放一維的資料就是錯誤的,二維的時候就應該存放二維的資料
			//當然你也不要去存放前i行前j列之和,這個相比於前面的就是三維的資料了
			//如果你算的是長方體的資料,這樣應該是對的,那麼我們的陣列也應該是三維的了 
		}
	}
	int max=INT_MIN;
	int sum; 
	for(int i=1;i<=N;i++)
	{
		for(int j=i;j<=N;j++)
		{   
	     	sum=0;
		//前面兩層迴圈是列迴圈,比如三列的話就是,1-2列,1-3列,2-3列 
			 for(int k=0;k<=N;k++)
			 {
			 //行迴圈,求和
			     if(sum<0) sum=0;
				 sum+=(Dy[k][j]-Dy[k][i-1]);
			     if(sum>max) max=sum;			 	
			 } 
			 
		}
	}
	cout<<max<<endl;
}