主要考察:记忆化搜索
第一问简单:直接从底向上搜索(或者自上到下也行),能到达最上面(或者最下面)就给1,不能就给0,最后扫描下低层情况。
搜索技巧:如果从一个点到另一个点,路劲一定会从上到下(从左到右),那么注意左右(上下)隐藏的关系。
第二问:根据题解从上到下搜索,查找到每个最上面点能达到的左右范围(递归+动规),想要用最少的储水点满足下面的干旱区,我们只需要从最左边开始,每次选包含当前位置的点且右边范围最大的储水点。
代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define Max 550
#define max(a,b) a>b?a:b;
#define min(a,b) a>b?b:a;
int a[Max][Max];
int map[Max][Max];
int left[Max][Max];
int right[Max][Max];
int n,m;
int X[]={-1,1,0,0},Y[]={0,0,-1,1};
int dfs(int y,int x) //行、列 搜索从上到下
{
map[y][x]=1;//标记走过
int next_x,next_y;
for(int i=0;i<4;i++)
{
next_x=x+X[i];
next_y=y+Y[i];
if(next_x>=0&&next_x<m&&next_y>=0&&next_y<n&&a[next_y][next_x]<a[y][x]) //没有出界且满足高到低
{
if(!map[next_y][next_x])
{
dfs(next_y,next_x);
}
left[y][x]=min(left[y][x],left[next_y][next_x]); //更新最左边的范围
right[y][x]=max(right[y][x],right[next_y][next_x]); //更新最右边的范围
}
}
}
int main()
{
scanf("%d%d",&n,&m);
memset(left,0x3f,sizeof(left));
for(int i=0;i<m;i++)
left[n-1][i]=right[n-1][i]=i; //初始化左右范围
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
scanf("%d",&a[i][j]);
}
}
for(int i=0;i<m;i++)
{
if(!map[0][i]) dfs(0,i); //走过的可以不走
}
int flag=0;
int count=0;
for(int i=0;i<m;i++)
{
if(!map[n-1][i]) //没有走过
{
flag=1;
count++;
}
}
if(flag)
{
printf("0\n%d\n",count);
return 0;
}
int num=0; //从最左边的点开始
while(num<m)
{
int max_r=0;
for(int i=0;i<m;i++)
{
if(left[0][i]<=num) //如果包含当前点
{
max_r=max(max_r,right[0][i]); //找到最右的储水点
}
}
count++;
num=max_r+1; //下一个点没包含进来
}
printf("1\n%d\n",count);
return 0;
}
看了题解才写出来.......题解传送门
......这题没写出来很不应该,这种情况隐约遇到过,也知道不是从上搜就是从下搜,因为我死于这种思路:
直接搜是不行的,需要记忆化,怎么记忆化才能最大化。
(我开始的搜索是从下往上走)
一:没有走过下个点,递归调用。
三:走过,下个点能走通。从当前点能走到水库就给当前点水库的标号(最上层之间也能用通道,可以减少水库数量),同时把干旱点保存到各个水库独自的集合,方便最后在O(m^2)时间内求解。但是其他方向也能走通,一个点有多个办法到达最上层........怎么保存,并且动态操作的复杂度小...........。
二:如果不能走通直接给-1,警示后面走到这个点,不要继续走了。