//376K 172MS G++
#include <stdio.h>
#include <string.h>
#define MAX 30
int W;
int H;
int board[MAX][MAX];
int Sx;
int Sy;
int Gx;
int Gy;
#define INF 99999
int dfs(int curX, int curY, int throwTime) {
int minThrow = INF;
if (throwTime > 10) {
return minThrow;
}
// first check if blocked immdiately
if (curY + 1 <= H-1 && board[curX][curY + 1] != 1) {
// up
int upNearest = -1;
for (int i = curY+1 ;i <= H-1; i++) {
if (board[curX][i] == 1) {
// the nearest block is in (curX, i)
// the ball stop and (curX, i-1)
upNearest = i - 1;
break;
}
}
if (upNearest == -1) {
if (curX == Gx && curY < Gy) { // can reach goal
if (throwTime < 10) {
return throwTime + 1;
}
}
} else {
if (upNearest >= Gy && curX == Gx && curY < Gy) { // can reach goal
if (throwTime < 10) {
return throwTime + 1;
}
}
}
if (upNearest > -1) { // if there is up block
board[curX][upNearest+1] = 0; // block is broken
int res = dfs(curX, upNearest, throwTime+1);
minThrow = minThrow < res ? minThrow : res;
board[curX][upNearest+1] = 1; // restore the block for other direction
}
}
// down,
//first check if blocked immdiately
if (curY-1 >=0 && board[curX][curY-1] != 1) {
int downNearest = -1;
for (int i = curY-1 ;i >= 0; i--) {
if (board[curX][i] == 1) {
// the nearest block is in (curX, i)
// the ball stop and (curX, i-1)
downNearest = i + 1;
break;
}
}
if (downNearest == -1) {
if (curX == Gx && curY > Gy) { // can reach goal
if (throwTime < 10) {
return throwTime + 1;
}
}
} else {
if (downNearest <= Gy && curX == Gx && curY > Gy) { // can reach goal
if (throwTime < 10) {
return throwTime + 1;
}
}
}
if (downNearest > -1) { // if there is up block
board[curX][downNearest-1] = 0; // block is broken
int res = dfs(curX, downNearest,throwTime+1);
minThrow = minThrow < res ? minThrow : res;
board[curX][downNearest-1] = 1; // restore the block for other direction
}
}
// left
//first check if blocked immdiately
if (curX-1 >= 0 && board[curX-1][curY] != 1) {
int leftNearest = -1;
for (int i = curX-1 ;i >= 0; i--) {
if (board[i][curY] == 1) {
// the nearest block is in (curX, i)
// the ball stop and (curX, i-1)
leftNearest = i + 1;
break;
}
}
if (leftNearest == -1) {
if (curY == Gy && curX > Gx) { // can reach goal
if (throwTime < 10) {
return throwTime + 1;
}
}
} else {
if (leftNearest <= Gx && curY == Gy && curX > Gx) { // can reach goal
if (throwTime < 10) {
return throwTime + 1;
}
}
}
if (leftNearest > -1) { // if there is up block
board[leftNearest-1][curY] = 0; // block is broken
int res = dfs(leftNearest, curY,throwTime+1);
minThrow = minThrow < res ? minThrow : res;
board[leftNearest-1][curY] = 1; // restore the block for other direction
}
}
// right
//first check if blocked immdiately
if (curX + 1 <= W-1 && board[curX+1][curY] != 1) {
int rightNearest = -1;
for (int i = curX+1 ;i <= W-1; i++) {
if (board[i][curY] == 1) {
// the nearest block is in (curX, i)
// the ball stop and (curX, i-1)
rightNearest = i - 1;
break;
}
}
if (rightNearest == -1) {
if (curY == Gy && curX < Gx) { // can reach goal
if (throwTime < 10) {
return throwTime + 1;
}
}
} else {
// printf("%d %d %d\n", Gx, curX, rightNearest);
if (rightNearest >= Gx && curY == Gy && curX < Gx) { // can reach goal
if (throwTime < 10) {
return throwTime + 1;
}
}
}
if (rightNearest > -1) { // if there is up block
board[rightNearest+1][curY] = 0; // block is broken
int res = dfs(rightNearest, curY,throwTime+1);
minThrow = minThrow < res ? minThrow : res;
board[rightNearest+1][curY] = 1; // restore the block for other direction
}
}
return minThrow;
}
void solve() {
int minThrow = dfs(Sx, Sy, 0);
if (minThrow == INF) {
printf("-1\n");
} else {
printf("%d\n", minThrow);
}
}
int main() {
while(1) {
scanf("%d %d", &W, &H);
if (W == 0 && H == 0) {
return 1;
}
memset(board, 0, sizeof(board));
for (int i = 0; i < H; i++) {
for (int j = 0; j < W; j++) {
int tmp;
scanf("%d", &tmp);
board[j][i] = tmp;
if (tmp == 2) {
Sx = j;
Sy = i;
// printf("S %d %d\n", Sx, Sy);
} else if (tmp == 3) {
Gx = j;
Gy = i;
// printf("G %d %d\n", Gx, Gy);
}
}
}
solve();
}
}
中规中矩的DFS题(用BFS应该也行吧,不过没想好如何保存board的状态,20*20 == 400 , 就算是状态压缩也没有这么大的整型数)。
题目还很贴心的告知了DFS最大的递归次数是12,可以剪枝来提高效率。
有一个比较细的规则,如果球的周围一格有block,那么就不能向该方向throw了,先做此判定,如果能throw,
再看在throw的一行/列上是否能有block将其停住,
如果没有,还要考虑虽然没有block将其停住,但是能经过终点将其停住的情况, 以及有block,但是throw先经过终点的情况。
如果有,并且没有经过终点,就从此block的临近一格(题目规定)开始新的BFS,同时将此block清掉,在此轮DFS返回以后,再恢复现场,
将block重新设上,就这样处理四个方向,得到四个方向能得到的最小throw次数(INF代表不能达到终点),
另外,本题也没有用到DFSvisitedFlag,应该是因为每次都会消去一格block,并且规定了最大剪枝,因此DFSvistedFlag可以不用(并且也没有合适的实现方式,
board的状态是没法用数组保存的)。