给定一个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;
}