POJ 1088

好吧,这道题也是关于搜索的,一开始看到我是没有任何思路的。不过经过了POJ1011的洗礼,我开始学会用搜索来解题目了。

题目大意:

Description

Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道载一个区域中最长底滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子
 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

输出最长区域的长度。
----------------------------------------------------------------我是华丽的分割线---------------------------------------------

这个问题似乎就是二维的最长连续增序列问题,不过,额……我倒是没有发现这道题目和一维最长连续增序列问题的相似处,也许我太水了吧……

首先肯定能用搜索解题目的,第一段我注释掉的代码就是用了搜索,没有任何剪枝,所以搜索会出现环路,并且重复了很多地方,最最坑的是我吧c[i][j]写成了c[i*row+j]而不是c[i*col+j],这简直是无法容忍的错误。主要还是我基本功不扎实啊,为了纪念这个错误,以及提醒自己很水,我决定还是保留这段。

后面就是我修改过后的版本了。具体思想如下:

数组a里面保存的是对应的高度,这个没得说。

数组b里面保存的是到目前为止,到达这个点的最长路径的长度。这个存在的意义是,只找最长的,要是短了那就不用在这个点上浪费时间了。这个优化的其实不是很多。

数组c里面保存的是从该点出发可以到的最长坡度。这个数组存在的意义就是当再次到达这个点的时候不用再进行递归搜索了,保证了每个点都只会搜索一次。这点想法来源于《算法导论》的动态规划。不过从这个角度说,我的解法还仍然是递归解法,这道题应该存在一种自低向上的真正的动态规划的解法。抱歉我水平有限……没有想出来……

这三个数组最大也就3*100*100*4=120000=120k应该不会超过要求。

搜索从某个点开始,深度优先搜索其临近的点中所有高度小于该点的点,然后到某个极小点为终点,然后退回,访问这个点的时候记录到达这个点的路径长度,然后每次从当前点退回的时候记录以这个点为起点搜索到的最长的坡度,这样下一次到达这个点的时候就不要再次搜索了。这相当于搜索了以改点为根的树,这个树的性质是所有的父节点的数据(也就是a[i][j])都大于子节点的数据,且父节点和子节点在原始数据中相邻。然后最长的路径实际上就是树的最高高度。

这里有一点,基于以上策略这个搜索空间可能是一个森林(而且还是相交的),解就是森林的最高高度,所以,一开始在我注释掉的程序里面我并没有意识到这个,所以我搜索了所有的临近的点,这样很不好,会产生环的问题,搜索基本上不太可能结束,搞的我自己最后都晕头转向。而后来,我去掉了所有else分支里面的递归,这样的话因为不可能存在a>b,b>c,c>a的这种非自然现象,所以也就没有环产生了,树就出来了,然后在主程序里面调用了一个for循环,对每个点(当然是还没有访问过的点)再次调用这个程序,这样就可以搜索了所有的树了。

这些是我写了东西以后才意识到的,所以,写完程序要总结啊啊。这道题目蛮经典的,真好!!

贴代码:

#include <stdio.h>
#include <stdlib.h>
int *a;
int maxPath;  //保存最大路径
int row;
int col;
int *b;   //数组,记录目前到达该节点的路径中最长的一条,-1表示未被访问过,0表示这个是最高点
int *c;   //数组,记录从该节点出发,找到的最长的路径,-1表示未被访问过,0表示这是死路或者极小点
/*
void getLongDown(int i,int j,int nowPath){
	
	int temp;
	temp=a[i*row+j];
	
	if (b[i*row+j]>=nowPath){
		return;
	}
	else {		
		b[i*row+j]=nowPath;
		}
	
	if (i>0)
	if (a[(i-1)*row+j]<temp) getLongDown(i-1,j,nowPath+1);
  else {maxPath=nowPath>maxPath?nowPath:maxPath;
  	    getLongDown(i-1,j,0);}
  	    
	
	if (j>0)
	if (a[i*row+j-1]<temp) getLongDown(i,j-1,nowPath+1);
  else  {maxPath=nowPath>maxPath?nowPath:maxPath;
  	     getLongDown(i,j-1,0);}
  
  if (i<row-1)	
  if (a[(i+1)*row+j]<temp) getLongDown(i+1,j,nowPath+1);
  else {maxPath=nowPath>maxPath?nowPath:maxPath;
  	    getLongDown(i+1,j,0);}
  
  if (j<col-1) 
  if (a[i*row+j+1]<temp) getLongDown(i,j+1,nowPath+1);
  else {maxPath=nowPath>maxPath?nowPath:maxPath;
  	    getLongDown(i,j+1,0);}
  	    
  return;
}*/

//居然再次搞错了,c[i][j]不是c[i*row+j]而是c[i*col+j]!!!!!!!!!!!!!!!
int getLongDown(int i,int j,int nowPath){
	
	int temp;
	int cTemp=0;
	int cMax=-1;
		
	if (c[i*col+j]!=-1)       //c[i][j]!=-1代表这个节点已经访问过并且计算了c[i][j]。
	  if (b[i*col+j]>=nowPath){
	  	return c[i*col+j];
	  }
	  else {  	
	  	b[i*col+j]=nowPath;
	  	temp=c[i*col+j]+b[i*col+j];
	  	maxPath=temp>maxPath?temp:maxPath;
	  	return c[i*col+j];
	  }
	
  b[i*col+j]=nowPath;	
	
	temp=a[i*col+j];
	if (i>0)
	  if (a[(i-1)*col+j]<temp) cTemp=getLongDown(i-1,j,nowPath+1)+1;
    else {maxPath=nowPath>maxPath?nowPath:maxPath;  
  	      cTemp=0;
    	    }
  
  cMax=cTemp>cMax?cTemp:cMax;
  	    	
	if (j>0)
	  if (a[i*col+j-1]<temp) cTemp=getLongDown(i,j-1,nowPath+1)+1;
    else  {maxPath=nowPath>maxPath?nowPath:maxPath;
    	     cTemp=0;
    	     }
  
  cMax=cTemp>cMax?cTemp:cMax;
  	
  if (i<row-1)	
    if (a[(i+1)*col+j]<temp) cTemp=getLongDown(i+1,j,nowPath+1)+1;
    else {maxPath=nowPath>maxPath?nowPath:maxPath;
    	    cTemp=0;
    	    }
  
  cMax=cTemp>cMax?cTemp:cMax;
  	
  
  if (j<col-1) 
    if (a[i*col+j+1]<temp) cTemp=getLongDown(i,j+1,nowPath+1)+1;
    else {maxPath=nowPath>maxPath?nowPath:maxPath;
    	   cTemp=0;
    	    }
  
  c[i*col+j]=cTemp>cMax?cTemp:cMax;
  
 // printf("%d %d %d %d %d here\n",i,j,nowPath,c[i*row+j],b[i*row+j]);
  
  return c[i*col+j];

}

int main(){
  int i,j;
  
  scanf("%d %d",&row,&col);
  maxPath=0;
  a=(int *)calloc(row*col,sizeof(int));
  b=(int *)calloc(row*col,sizeof(int));
  c=(int *)calloc(row*col,sizeof(int));
  
  for (i=0;i<row;i++){
    for(j=0;j<col;j++){
      scanf("%d",&(a[i*col+j])); 
      b[i*col+j]=-1;  
      c[i*col+j]=-1;  
    }
  }
  
  for (i=0;i<row*col;i++){               //第一段程序没有这个for循环
    if (c[i]==-1)  getLongDown(i/col,i%col,0);
  }
  //getLongDown(0,0,0);

  free(a);
  free(b);
  free(c);
  printf("%d\n",maxPath+1);
}





题目描述 给出一个$n\times m$的矩阵,每个位置上有一个非负整数,代表这个位置的海拔高度。一开始时,有一个人站在其中一个位置上。这个人可以向上、下、左、右四个方向移动,但是只能移动到海拔高度比当前位置低或者相等的位置上。一次移动只能移动一个单位长度。定义一个位置为“山顶”,当且仅当从这个位置开始移动,可以一直走到海拔高度比它低的位置上。请问,这个矩阵中最多有多少个“山顶”? 输入格式 第一行两个整数,分别表示$n$和$m$。 接下来$n$行,每行$m$个整数,表示整个矩阵。 输出格式 输出一个整数,表示最多有多少个“山顶”。 样例输入 4 4 3 2 1 4 2 3 4 3 5 6 7 8 4 5 6 7 样例输出 5 算法1 (递归dp) $O(nm)$ 对于这道题,我们可以使用递归DP来解决,用$f(i,j)$表示以$(i,j)$为起点的路径最大长度,那么最后的答案就是所有$f(i,j)$中的最大值。 状态转移方程如下: $$ f(i,j)=\max f(x,y)+1(x,y)是(i,j)的下一个满足条件的位置 $$ 注意:这里的状态转移方程中的$x,y$是在枚举四个方向时得到的下一个位置,即: - 向上:$(i-1,j)$ - 向下:$(i+1,j)$ - 向左:$(i,j-1)$ - 向右:$(i,j+1)$ 实现过程中需要注意以下几点: - 每个点都需要搜一遍,因此需要用双重for循环来枚举每个起点; - 对于已经搜索过的点,需要用一个数组$vis$来记录,防止重复搜索; - 在进行状态转移时,需要判断移动后的点是否满足条件。 时间复杂度 状态数为$O(nm)$,每个状态转移的时间复杂度为$O(1)$,因此总时间复杂度为$O(nm)$。 参考文献 C++ 代码 算法2 (动态规划) $O(nm)$ 动态规划的思路与递归DP类似,只不过转移方程和实现方式有所不同。 状态转移方程如下: $$ f(i,j)=\max f(x,y)+1(x,y)是(i,j)的下一个满足条件的位置 $$ 注意:这里的状态转移方程中的$x,y$是在枚举四个方向时得到的下一个位置,即: - 向上:$(i-1,j)$ - 向下:$(i+1,j)$ - 向左:$(i,j-1)$ - 向右:$(i,j+1)$ 实现过程中需要注意以下几点: - 每个点都需要搜一遍,因此需要用双重for循环来枚举每个起点; - 对于已经搜索过的点,需要用一个数组$vis$来记录,防止重复搜索; - 在进行状态转移时,需要判断移动后的点是否满足条件。 时间复杂度 状态数为$O(nm)$,每个状态转移的时间复杂度为$O(1)$,因此总时间复杂度为$O(nm)$。 参考文献 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值