好吧,这道题也是关于搜索的,一开始看到我是没有任何思路的。不过经过了POJ1011的洗礼,我开始学会用搜索来解题目了。
题目大意:
Description
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
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);
}