求二维整数数组中最长的递减数字串(滑雪算法)

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值