poj 1050-小白演算法練習 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
Output
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項和和最大值就好了。
當然也有人是這樣寫的:
- int MaxSum(int n,int *a)
- {
- int sum=0,b=0;
- for(int i=1; i<=n; i++)
- {
- if(b>0)
- {
- b+=a[i];
- }
- else
- {
- b=a[i];
- }
- if(b>sum)
- {
- sum = b;
- }
- }
- 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;
}