給定一個m行n列的整數數組,求解該數組中最長的遞減數字串。數字串只能由相鄰的數字連接獲得。比如數組a爲
[1 2 3 4
8 7 6 5
6 5 4 2]
a[1][1]==7, 則連接在a[1][1]之後數字只可能是a[0][1],a[1][2],a[2][1]。a中最長的遞減數字串爲8,7,6,5,4,3,2,1。
這是前幾天在某網絡公司筆試時碰到的一道題目。當時只想出瞭解題思路,沒時間寫代碼。現在把思路和後來寫的代碼跟大家分享一下。
或許大家都遇到過讓你求解一維整數數組中最長的連續遞減子字符串的問題。與一維數組中的問題一樣,該問題也是一個動態規劃問題。
假設最終求得的最長遞減數字串爲a[i1][j1]->a[i2][j2]->...a[ik][jk],長度爲k。可以證明以a[i2][j2]爲最大數字的遞減數字串長度必然爲k-1,而形式爲
a[i2][j2]->a[i3][j3]->...a[ik][jk]。否則,必然可以找到一個長度大於k的以a[i1][j1]開頭的數字串。因此可以先確定以a[i][j]周圍的數字a[i-1][j],a[i+1][j],a[i][j-1],
a[i][j+1]中比a[i][j]小的數爲第一個數字的最長數字串,然後從中選擇長度最長的一個數字串,如以a[i-1][j]開頭的最長遞減數字串。再將a[i][j]加在以a[i-1][j]開頭的
最長遞減數字串的前面,從而得到以a[i][j]爲起始數字的最長遞減數字串。求解以a[i][j]爲首數字的最長遞減數字串問題,可以通過先求解以a[i-1][j],a[i+1][j],a[i][j-1],
a[i][j+1]中比a[i][j]小的數爲第一個數字的最長數字串這一子問題,再將a[i][j]添加到子問題求解中得到的最長的遞減數字串的前面,從而得到最終解。該問題符合
動態規劃問題的兩大特徵,即通過合併最優子問題得到問題的解和子問題的求解存在重疊。
下面是解決該問題的基本思路。創建兩個數組length[][],path[][]。length[i][j],path[i][j]分別記錄以a[i][j]爲首數字的最長遞減數字串的長度和該最長遞減數字串下一個
數字的位置。path[i][j]的取值爲0,1,2,3,-1,分別表示最長遞減數字串的下一個數字在path[i][j]的上方,下方,左方,右方,不存在下一個數字。由於以任意的a[i][j]
爲首數字的最長遞減數字串的長度和下一個數字位置只求解一次,因此該解法的時間複雜度爲O(mn)。代碼如下所示#include<iostream>
using namespace std;
int row, col;
void find(int **a, int row, int col);
void dfs(int **a, int i, int j, int **length, int **path);
int main()
{
cin>>row>>col;
int **a=new int *[row];
for(int i=0; i<row; i++)
a[i]=new int[col];
for(int i=0; i<row; i++)
{
for(int j=0; j<col; j++)
cin>>a[i][j];
//a[i][j]=i*col+j;
}
for(int i=0; i<row; i++)
{
for(int j=0; j<col; j++)
//cin>>a[i][j];
cout<<a[i][j]<<' ';
cout<<endl;
}
find(a, row, col);
system("pause");
}
// path: 0--上;1--下;2--左;3--右;-1--未確定
void find(int **a, int row, int col)
{
int **length=new int *[row];
for(int i=0; i<row; i++)
length[i]=new int[col];
for(int i=0; i<row; i++)
{
for(int j=0; j<col; j++)
length[i][j]=0;
}
int **path=new int *[row];
for(int i=0; i<row; i++)
path[i]=new int[col];
for(int i=0; i<row; i++)
{
for(int j=0; j<col; j++)
path[i][j]=-1;
}
for(int i=0; i<row; i++)
{
for(int j=0; j<col; j++)
{
if(0==length[i][j])
{
dfs(a, i, j, length, path);
}
}
}
for(int i=0; i<row; i++)
{
for(int j=0; j<col; j++)
cout<<length[i][j]<<' ';
cout<<endl;
}
for(int i=0; i<row; i++)
{
for(int j=0; j<col; j++)
cout<<path[i][j]<<' ';
cout<<endl;
}
int MAXL=0;
int IDX_i=0;
int IDX_j=0;
for(int i=0; i<row; i++)
{
for(int j=0; j<col; j++)
{
if(length[i][j]>MAXL)
{
MAXL=length[i][j];
IDX_i=i;
IDX_j=j;
}
}
}
do
{
cout<<a[IDX_i][IDX_j]<<' ';
switch(path[IDX_i][IDX_j])
{
case 0:IDX_i--;break;
case 1:IDX_i++;break;
case 2:IDX_j--;break;
case 3:IDX_j++;break;
}
}while(path[IDX_i][IDX_j]!=-1);
cout<<a[IDX_i][IDX_j]<<endl;
}
void dfs(int **a, int i, int j, int **length, int **path)
{
int temp[4];
if(i>0 && a[i][j]>=a[i-1][j])
{
if(0==length[i-1][j])
dfs(a, i-1, j, length, path);
temp[0]=length[i-1][j];
}
else
temp[0]=-1;
if(i<row-1 && a[i][j]>=a[i+1][j])
{
if(0==length[i+1][j])
dfs(a, i+1, j, length, path);
temp[1]=length[i+1][j];
}
else
temp[1]=-1;
if(j>0 && a[i][j]>=a[i][j-1])
{
if(0==length[i][j-1])
dfs(a, i, j-1, length, path);
temp[2]=length[i][j-1];
}
else
temp[2]=-1;
if(j<col-1 && a[i][j]>=a[i][j+1])
{
if(0==length[i][j+1])
dfs(a, i, j+1, length, path);
temp[3]=length[i][j+1];
}
else
temp[3]=-1;
for(int IDX_i=0; IDX_i<4; IDX_i++)
{
if(temp[IDX_i]!=-1)
{
if(temp[IDX_i]+1>length[i][j])
{
length[i][j]=temp[IDX_i]+1;
path[i][j]=IDX_i;
}
}
}
if(length[i][j]==0)
length[i][j]=1;
}