Description
Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道载一个区域中最长底滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-...-3-2-1更长。事实上,这是最长的一条。
1 2 3 4 5 16 17 18 19 6 15 24 25 20 7 14 23 22 21 8 13 12 11 10 9
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-...-3-2-1更长。事实上,这是最长的一条。
Input
输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。
Output
输出最长区域的长度。
Sample Input
5 5 1 2 3 4 5 16 17 18 19 6 15 24 25 20 7 14 23 22 21 8 13 12 11 10 9
Sample Output
25
方法一(DFS);
ps:主要就是剪枝了,先找出数值最大的点和最小的点搜到的结果ans,再搜索每一个点,先预判一下该点最优情况的结果是否大于ans,然后搜索时每步预判一下(如果最大的点-当前点的值+当前步数<ans,剪掉)就好了
代码:
#include<stdio.h>
#include<string.h>
const int INF=0x3f3f3f3f;
int n,m,ans,cnt,maxn;//cnt为最小的点,maxn为最大的点
int s[110][110];
int vis[110][110];
int to[][2]= {0,1,0,-1,1,0,-1,0};
void dfs(int x,int y,int step)//除了最小的点以外的DFS
{
if(s[x][y]-cnt+step<ans)//预判最大的结果是否>ans
return ;
if(step>ans)
ans=step;
for(int i=0; i<4; i++)
{
int xx=x+to[i][0],yy=y+to[i][1];
if(xx>=0&&xx<n&&yy>=0&&yy<m&&!vis[xx][yy]&&s[xx][yy]<s[x][y])
{
vis[xx][yy]=1;
dfs(xx,yy,step+1);
vis[xx][yy]=0;
}
}
}
void _dfs(int x,int y,int step)//最小的点的DFS
{
if(maxn-s[x][y]+step<ans)//预判
return ;
if(step>ans)
ans=step;
for(int i=0; i<4; i++)
{
int xx=x+to[i][0],yy=y+to[i][1];
if(xx>=0&&xx<n&&yy>=0&&yy<m&&!vis[xx][yy]&&s[xx][yy]>s[x][y])
{
vis[xx][yy]=1;
_dfs(xx,yy,step+1);
vis[xx][yy]=0;
}
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
ans=0,cnt=INF,maxn=-1;
int i,j,x1,y1,x2,y2;
for(i=0; i<n; ++i)
{
for(j=0; j<m; ++j)
{
scanf("%d",&s[i][j]);
if(s[i][j]<cnt)
cnt=s[i][j],x1=i,y1=j;
if(s[i][j]>maxn)
maxn=s[i][j],x2=i,y2=j;
}
}
memset(vis,0,sizeof(vis));//找出最大的点的ans
vis[x2][y2]=1;
dfs(x2,y2,1);
if(maxn-cnt>=ans)//预判
{
memset(vis,0,sizeof(vis));
vis[x1][y1]=1;
_dfs(x1,y1,1);//找出最小的点的ans
}
for(i=0; i<n; ++i)
{
for(j=0; j<m; ++j)
{
if(s[i][j]-cnt<ans)//预判最大的结果是否>ans
continue;
memset(vis,0,sizeof(vis));
vis[i][j]=1;
dfs(i,j,1);
}
}
printf("%d\n",ans);
}
return 0;
}
ps:感觉还是有点慢啊,于是百度了一下最优代码,一看吓一跳,我靠 ,原来是个DP。。。。
方法二(DP)
ps:
DP思想:dp[i][j]=max(dp[i-1][j],dp[i][j-1],dp[i+1][j],dp[i][j+1])+1
直接记忆化搜索,用dp[i][j]保存每个点的最大值
代码:
#include<stdio.h>
#define max(a,b) (a>b?a:b)
int n,m;
int dp[110][110];//记录每个点的最大值
int map[110][110];
int to[][2]= {0,1,0,-1,1,0,-1,0};
int DP(int x,int y)
{
int cnt=0;
if(dp[x][y])//如果不是0,表示已经找到了该点的最大值,直接返回,避免重复计算
return dp[x][y];
for(int i=0; i<4; ++i)
{
int xx=x+to[i][0],yy=y+to[i][1];
if(xx<0||xx>=n||yy<0||yy>=m)
continue;
if(map[xx][yy]<map[x][y])
cnt=max(cnt,DP(xx,yy));//寻找四个方向中满足条件的最大值
}
dp[x][y]=cnt+1;//其本身的最大值=四个方向中的最大值+1
return dp[x][y];
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
int i,j;
for(i=0; i<n; ++i)
for(j=0; j<m; ++j)
{
scanf("%d",&map[i][j]);
dp[i][j]=0;
}
int ans=0;
for(i=0; i<n; ++i)
for(j=0; j<m; ++j)
ans=max(ans,DP(i,j));
printf("%d\n",ans);
}
return 0;
}