题意:在一个二维的数组里面,搜索出一条按降序排列的最长子序列,输出长度,如果像我之前对于每个点都进行都搜索一次,明显就会重复经过很多点,然后TLE。所以既可以采用记忆化搜索,用一个数组保存已经搜过的点,也可以用动态规划推递推方程。
记忆化搜索,搜索递归的退出条件是达到最长的的降序子序列的长度,并返回保存在数组里,实现记载,下次遇到该点就不用再搜一次!
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[110][110],M[110][110],next[4][2]={{-1,0},{0,1},{1,0},{0,-1}},n,m;
int dfs(int x,int y)
{
if(dp[x][y])
return dp[x][y];
int sum=1;
for(int i=0;i<4;i++)
{
int tx=x+next[i][0];
int ty=y+next[i][1];
if(tx<0||ty<0||tx>=n||ty>=m||M[tx][ty]>=M[x][y])
continue;
sum=max(sum,dfs(tx,ty)+1);
}
return dp[x][y]=sum;
}
int main()
{
while(~scanf("%d %d",&n,&m))
{
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
scanf("%d",&M[i][j]);
memset(dp,0,sizeof(dp));
int maxx=-1;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
maxx=max(maxx,dfs(i,j));
printf("%d\n",maxx);
}
}
**虽然,这题我跟倾向于记忆化搜索(代码少),最近刚入门dp,刚好看到有这题的分类,再奉上dp推出递推方程式的写法。
有两种dp的写法,一种 “我为人人”,的思想就是用自己去更新周围相关的状态,比如这题如果周围遇到比自己大的点,就尝试去更新周围点加+1,注意这题更新的方向是从小到大。
将所有点按高度从小到大排序。(规定搜索的方向,从最小的点更新比他大的点的状态,必须要) 每个点的 L 值都初始化为 1
从小到大遍历所有的点。经过一个点(i,j)时,要更新他周围的,比它高的点
的L值。例如:**
**if H(i+1,j) > H(i,j) // H代表高度
dp(i+1,j) = max(dp(i+1,j),dp(i,j)+1)**
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct Node
{
int x,y,h;
}node[10010];
int Next[4][2]={{-1,0},{0,1},{1,0},{0,-1}},height[110][110],dp[110][110];
bool cmp(Node a,Node b)
{
return a.h<b.h;
}
int main()
{
int n,m;
#ifdef yexiaoju
freopen("Untitled2.txt","r",stdin);
#endif // yexiaoju
while(~scanf("%d %d",&n,&m))
{
int num=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&height[i][j]);
node[num].x=i,node[num].y=j,node[num++].h=height[i][j];
dp[i][j]=1;
}
}
sort(node,node+num,cmp);
for(int i=0;i<n*m;i++)
{
for(int j=0;j<4;j++)
{
int tx=node[i].x+Next[j][0];
int ty=node[i].y+Next[j][1];
if(tx>0&&ty>0&&tx<=n&&ty<=m&&height[tx][ty]>node[i].h)
{
dp[tx][ty]=max(dp[tx][ty],dp[node[i].x][node[i].y]+1);
}
}
}
int Max=-1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(dp[i][j]>Max)
Max=dp[i][j];
printf("%d\n",Max);
}
}
还有一种 人人为我,思想 用别人的状态来更新自己的状态,比如这题,每一个点搜索遇到周围有比他小的,就判断一下
dp[x][y]=max(dp[x][y],dp[nextx][nexty]+1);
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct Node
{
int x,y,h;
}node[10010];
int Next[4][2]={{-1,0},{0,1},{1,0},{0,-1}},height[110][110],dp[110][110];
bool cmp(Node a,Node b)
{
return a.h<b.h;
}
int main()
{
int n,m;
#ifdef yexiaoju
freopen("Untitled2.txt","r",stdin);
#endif // yexiaoju
while(~scanf("%d %d",&n,&m))
{
int num=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&height[i][j]);
node[num].x=i,node[num].y=j,node[num++].h=height[i][j];
dp[i][j]=1;
}
}
sort(node,node+num,cmp);
for(int i=0;i<n*m;i++)
{
for(int j=0;j<4;j++)
{
int tx=node[i].x+Next[j][0];
int ty=node[i].y+Next[j][1];
if(tx>0&&ty>0&&tx<=n&&ty<=m&&height[tx][ty]<node[i].h)
{
dp[node[i].x][node[i].y]=max(dp[node[i].x][node[i].y],dp[tx][ty]+1);
}
}
}
int Max=-1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(dp[i][j]>Max)
Max=dp[i][j];
printf("%d\n",Max);
}
}