开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第24天,点击查看活动详情
在大家小时候不知道有没有玩过下图所示游戏
在这里我们可以看出来,图上有一个黑色炸弹,两种墙,和一些怪兽,那么我们要在哪里放置炸弹才能消灭最多怪兽呢?
我们可以把地图模型化:墙用‘#’表示,怪兽用“@”表示,空地用‘.’表示,炸弹只能在空地上。
首先我们用一个二维数组来存储地图,那么炸弹位置我们可以一个一个尝试。已知爆炸方向沿着上下左右四个方向,那么我们可以对每个点枚举,然后统计上下左右有几个怪兽,最后输出最多的一条路。
怎么判断一个方向上的消灭怪兽判断?
while(a[x][y]!='#'){
if(a[x][y]=='@'){
sum++}
x++;
}
定义地图数组a,里面x表示第x行,y表示第y列,那么当当前值不是墙,并且该位置有怪物,那么怪物数增加,往下一行继续判断,直到碰到墙。
那么代码是不是有思路了呢。
首先声明一个字符数组,然后通过键盘或者直接赋值得到地图,接下来进行循环遍历,如果当前是空地,就可以尝试放炸弹,进行一个方向上消灭怪兽判断,包括四个:上下左右,代码都是类似的
但是在这种代码里面,我们可能遇到一个问题,就是小人没办法到达那个点。所以我们还可以通过广度优先来枚举.
伪算法:
初始位置入队列,标记初始位置已经走过置为1,判断该位置的炸弹最大威力并记录
开始上下左右循环,每一次都以队列首元素为初始出发点,扩展位置入队列(扩展时也需要判断是否越界,能否安装炸弹以及是否已经走过)满足条件则判断该点的最大威力 与当前最大威力对比,大于则替换之
然后队首元素出队,如此往复循环
伪代码:
首先,我们的初始点为(3,3)我们需要将其标记置为1,表示该点已经走过,判断炸弹在该点的威力,然后赋值给最大当前威力,并保存该点,
开始循环向上下左右四个方向分别尝试
{
判断扩展的点是否越界,查出地图,查出则进行下一个方向的尝试
判断该点是否已经走过并且判断该点是否平地(只有平地才能放炸弹),如果该点没有走过且可以放炸弹
则以此点为初始点 进一步 按原策略搜索
}
搜索完毕后返回
按照伪代码:那么dfs如下:
public static void dfs(int x, int y) {
int sum, tx, ty;
//四个方向
int[][] next = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
//得到当前位置消灭敌人的数量
sum = getsum(x, y);
//更新最大消灭敌人的数量
if (sum > max) {
max = sum;
mx = x;
my = y;
}
for (int i = 0; i < 4; i++) {
tx = x + next[i][0];
ty = y + next[i][1];
if (tx < 0 || tx > n - 1 || ty < 0 || ty > m - 1) {
continue;
}
if (a[tx][ty] == '.' && book[tx][ty] == 0) {
book[tx][ty] = 1;
dfs(tx, ty);
}
}
return;
}
getsum 其实就是判断一个方向上怪兽
广度优先搜索的精髓在于不一条路走到黑,而是广泛的考虑下一步走到的每一个点,而这样的方式可以很好的得到类似最小值最短路径这种问题,像是一层一层的向下走,可以类比从内向外拨开洋葱皮一样,当你到达你的目的时,显然这就时最短的路径,因为所有扩展路径先前走过的长度都是一样的。