poj3009urling 2.0(AC2)

urling 2.0
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 15158 Accepted: 6299

Description

On Planet MM-21, after their Olympic games this year, curling is getting popular. But the rules are somewhat different from ours. The game is played on an ice game board on which a square mesh is marked. They use only a single stone. The purpose of the game is to lead the stone from the start to the goal with the minimum number of moves.

Fig. 1 shows an example of a game board. Some squares may be occupied with blocks. There are two special squares namely the start and the goal, which are not occupied with blocks. (These two squares are distinct.) Once the stone begins to move, it will proceed until it hits a block. In order to bring the stone to the goal, you may have to stop the stone by hitting it against a block, and throw again.


Fig. 1: Example of board (S: start, G: goal)

The movement of the stone obeys the following rules:

  • At the beginning, the stone stands still at the start square.
  • The movements of the stone are restricted to x and y directions. Diagonal moves are prohibited.
  • When the stone stands still, you can make it moving by throwing it. You may throw it to any direction unless it is blocked immediately(Fig. 2(a)).
  • Once thrown, the stone keeps moving to the same direction until one of the following occurs:
    • The stone hits a block (Fig. 2(b), (c)).
      • The stone stops at the square next to the block it hit.
      • The block disappears.
    • The stone gets out of the board.
      • The game ends in failure.
    • The stone reaches the goal square.
      • The stone stops there and the game ends in success.
  • You cannot throw the stone more than 10 times in a game. If the stone does not reach the goal in 10 moves, the game ends in failure.


Fig. 2: Stone movements

Under the rules, we would like to know whether the stone at the start can reach the goal and, if yes, the minimum number of moves required.

With the initial configuration shown in Fig. 1, 4 moves are required to bring the stone from the start to the goal. The route is shown in Fig. 3(a). Notice when the stone reaches the goal, the board configuration has changed as in Fig. 3(b).


Fig. 3: The solution for Fig. D-1 and the final board configuration

Input

The input is a sequence of datasets. The end of the input is indicated by a line containing two zeros separated by a space. The number of datasets never exceeds 100.

Each dataset is formatted as follows.

the width(=w) and the height(=h) of the board
First row of the board

...
h-th row of the board

The width and the height of the board satisfy: 2 <= w <= 20, 1 <= h <= 20.

Each line consists of w decimal numbers delimited by a space. The number describes the status of the corresponding square.

0vacant square
1block
2start position
3goal position

The dataset for Fig. D-1 is as follows:

6 6
1 0 0 2 1 0
1 1 0 0 0 0
0 0 0 0 0 3
0 0 0 0 0 0
1 0 0 0 0 1
0 1 1 1 1 1

Output

For each dataset, print a line having a decimal integer indicating the minimum number of moves along a route from the start to the goal. If there are no such routes, print -1 instead. Each line should not have any character other than this number.

Sample Input

2 1
3 2
6 6
1 0 0 2 1 0
1 1 0 0 0 0
0 0 0 0 0 3
0 0 0 0 0 0
1 0 0 0 0 1
0 1 1 1 1 1
6 1
1 1 2 1 1 3
6 1
1 0 2 1 1 3
12 1
2 0 1 1 1 1 1 1 1 1 1 3
13 1
2 0 1 1 1 1 1 1 1 1 1 1 3
0 0

Sample Output

1
4
-1
4
10
-1


因为本题不超过20,所以可以用DFS,如果超过20的话,就要想别的方法,否则是很容易就超时的
感受:
自己最开始的时候写dfs函数的时候的返回值是int,仔细想想 int dfs();这个是不对的
个人理解是有返回值的话,就是有结果就要返回,但是这个是需要遍历所有的方法之后才能得到最小值的
像stick那题,是有返回值的,那是因为它是从大到小排列,如果找到组合就退出说明这个长度是可以的
 
做题过程:做着做着就不对了,昨天还想着是对的,调着调着就不对了,还是基本功啊
参考:http://blog.csdn.net/lyy289065406/article/details/6647671
 
/*第一次提交wa 后来用15组数据的时候,发现倒数第2组数据一起算总是7,但是如果这组放到第一个就是-1,正确答案就是-1,原因是自己初始化的时候都只初始化本次的行宽,因此上一次有些残留数据还在,这样就导致本来一直一个方向的话就会出界的问题就不存在了,这个问题要严重关注*/
 
测试数据 
2 1
3 2
6 6
1 0 0 2 1 0
1 1 0 0 0 0
0 0 0 0 0 3
0 0 0 0 0 0
1 0 0 0 0 1
0 1 1 1 1 1
6 1
1 1 2 1 1 3
6 1
1 0 2 1 1 3
12 1
2 0 1 1 1 1 1 1 1 1 1 3
13 1
2 0 1 1 1 1 1 1 1 1 1 1 3
20 20
0 1 0 0 0 0 1 0 1 0 0 1 0 2 0 1 0 1 0 0
1 0 0 1 0 0 0 0 0 0 0 0 0 1 1 1 0 1 0 1
0 0 1 0 1 0 0 0 0 1 1 0 0 1 1 0 0 0 1 0
0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1 0 0 1
0 0 0 0 0 1 1 0 0 0 0 0 1 0 0 1 1 0 0 1
0 0 0 0 0 0 1 0 0 1 1 0 0 1 0 1 0 0 0 1
1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1
1 1 0 0 0 0 0 1 0 0 0 0 0 0 3 0 1 0 0 0
0 0 1 1 0 0 1 0 1 1 0 0 0 0 0 1 0 1 1 0
1 1 0 1 1 0 0 1 0 0 0 0 1 0 0 0 1 1 0 0
0 0 0 1 1 1 0 0 0 0 0 0 1 1 1 0 0 1 0 0
1 0 1 0 0 1 0 1 0 1 0 1 0 0 0 1 0 1 0 0
0 1 1 0 0 0 1 0 0 1 0 1 1 1 0 1 0 1 0 0
0 1 0 0 1 1 0 1 0 1 0 1 0 1 1 1 1 1 0 1
1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0
0 0 1 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 1 0 1 0 1 1 0 1 1 0 1 1 1
1 1 0 0 0 1 0 0 1 0 1 0 1 0 1 0 0 0 0 1
0 1 0 0 1 0 1 0 0 0 1 0 0 1 0 0 0 0 0 1
1 0 1 1 1 0 0 0 0 1 0 1 0 0 0 0 1 1 0 1
0 0
 
/*AC代码*/
#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

#define MAXINT 30

int map[MAXINT][MAXINT];

/*第一次提交wa 后来用15组数据的时候,发现倒数第2组数据一起算总是7,但是如果这组放到第一个就是-1,正确答案就是-1,原因是自己初始化的时候都只初始化本次的行宽,因此上一次有些残留数据还在,这样就
导致本来一直一个方向的话就会出界的问题就不存在了,这个问题要严重关注*/


int startx = 0;
int starty = 0;
int endx = 0;
int endy = 0;
int w = 0;
int h = 0;
int wtmp = 0;
int htmp = 0;

int ans = 9999;

int dir[4][2] = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };

//自己最开始的时候写的返回值是int,仔细想想
//个人理解是有返回值的话,就是有结果就要返回,但是这个是需要遍历所有的方法之后才能得到最小值的
//像stick那题,是有返回值的,那是因为它是从大到小排列,如果找到组合就退出说明这个长度是可以的
//静止中遇到石头是走不通的,只能在运动中遇到石头然后才会使石头消失,然后更改方向,
//自己还有一个理解错的地方认为是如果遇到石头就一定要从另一个方向走,这样是不对的,可以继续走
//入参yongdong 0-表示静止,1--表示运动中
void dfs(int x, int y, int yundong, int step)
{
	int i = 0;
	int x1 = 0;
	int y1 = 0;
	int x2 = 0;
	int y2 = 0;
	if (x<1 || x>h || y<1 || y>w) return ;
	if ((x == endx) && (y == endy) && (step <= 10))
	{
		if (ans > step) ans = step;
		//printf("1:step = %d,ans = %d",step, ans);
	}

	//这个最重要的剪枝,自己最开始写代码也没加,这个应该是最开始就要加的呀
	if (step >10)
	{
		return ;
	}

	for (i = 0; i < 4; i++)
	{
		//if (i == diretion) continue; //如果是从这个反向过来的,就不查这个方向了,这个判断就是不对的,如果遇到一个石头,则石头消失,还是可以继续从这个方向走的
		x1 = x + dir[i][0];
		y1 = y + dir[i][1];
		if (x1<1 || x1>h || y1<1 || y1>w) continue;

		if (3 == map[x1][y1] && ((step + 1) <= 10))
		{
			if (ans > (step + 1)) ans = step + 1;
			return;
		}

		//如果这个方向走的第一步就是石头,则不能走
		if (0 == yundong)
		{
			if (1 == map[x1][y1]) continue; 
		}

		while (0 == map[x1][y1])
		{
			x2 = x1;
			y2 = y1;
			x1 = x2 + dir[i][0];
			y1 = y2 + dir[i][1];
			if (x1<1 || x1>h || y1<1 || y1>w)
			{
				//说明这个方向不对,都出界了还没遇到石头或遇到目标,这条路不能走
				//看有没有别的方向可以走
				break;
			}
			if (3 == map[x1][y1] && ((step + 1) <= 10))
			{
				if (ans > (step + 1)) ans = step + 1;
				return;
			}
		}
		if (1 == map[x1][y1])
		{
			map[x1][y1] = 0;//石头会消失
			dfs(x2, y2, 0, step + 1);
			map[x1][y1] = 1;
		}
	}
	return ;
}

void init()
{
	int i = 0;
	int j = 0;
	ans = 9999;
	for (i = 1; i <= htmp; i++)
	{
		for (j = 1; j <= wtmp; j++)
		{
			map[i][j] = 999;
		}
	}
	startx = 0;
	starty = 0;
	endx = 0;
	endy = 0;
	return;
}

int main()
{
	int i = 0;
	int j = 0;
	int k = 0;
	freopen("input.txt", "r", stdin);
	while (2 == scanf("%d %d", &w, &h) && 0 != w && 0 != h)
	{
		if (0 == k)
		{
			htmp = h;
			wtmp = w;
		}
		init();
		if (1 != k)
		{
			htmp = h;
			wtmp = w;
		}
		k += 1;
		for (i = 1; i <= h; i++)
		{
			for (j = 1; j <= w; j++)
			{
				scanf("%d", &map[i][j]);
				if (2 == map[i][j])
				{
					startx = i;
					starty = j;
					map[i][j] = 0;
				}
				else if (3 == map[i][j])
				{
					endx = i;
					endy = j;
				}
			}
		}
		dfs(startx, starty, 0,0); 
		if (9999 == ans) ans = -1;
		printf("%d\n", ans);
	}
	return 0;
}

 

 

 

//第二遍20150112

//要么往x方向要么往y方向
//求最小移动方向,那可以dfs遍历所有情况,然后取最小值
//自己在做第二遍的时候对那个一直往一个方向走这个不好把握,比如这次是向下走的,那我下一步就不能向上走了,否则不就一直在走重复的路吗
//pojAC 第2遍做的感受是对一个方向的把握不好,感觉就不敢往下写了,最终还是有参考了,要继续强化
#define MAXINTN 30
#define MAXANS  10000

int map[MAXINTN][MAXINTN];
int w = 0;
int h = 0;
int startx = 0;
int starty = 0;
int endx   = 0;
int endy   = 0;
int ans    = 0;

int dir[4][2] = { { -1, 0 }, { 1, 0 }, { 0, 1 }, {0,-1} };

void init()
{
	int i = 0;
	int j = 0;
	for (i = 0; i < MAXINTN; i++)
	{
		for (j = 0; j < MAXINTN; j++)
		{
			map[i][j] = 0;
		}
	} 
	startx = 0;
	starty = 0;
	endx   = 0;
	endy   = 0;
	ans    = MAXANS;
	return;
}

//第二次写的时候没加这个status,因为这个要判断是否是在路上遇到的石头,还是在前进过程中遇到的
int dfs(int x, int y,int status, int num) //status:0是静止,1:运动,其实也可以不用,其实每个方向的一个就判断下是不是石头,也是一样的效果
{
	int i = 0;
	int x1 = 0;
	int x2 = 0;
	int y1 = 0;
	int y2 = 0;
	if ((x <1)||(x>w)||(y<1)||(y>h))
	{
		return 0;
	}
	if (num>10)
	{
		return 0;
	}
	if ((x == endx)&&(y == endy)&&(num <=10))
	{
		if (ans > num)
		{
			ans = num;
		}
		return 1;
	}

	for (i = 0; i < 4;i++)
	{
		//一直朝着这个方向走
		x1 = x + dir[i][0];
		y1 = y + dir[i][1];
		if ((x1 <1) || (x1>w) || (y1<1) || (y1>h)) continue;//越界,换个方向
		if ((0 == status) && (1 == map[x1][y1]))
		{
			continue; //如果一开始就遇到了石头,那肯定这个方向就不行了呀,换另一个方向
		}

		//第一步就遇到了终点
		if (3 == map[x1][y1]) //到终点了
		{
			if ((ans > (num + 1)) && ((num + 1) <= 10))
			{
				ans = num + 1;
				return 1;
			}
			else
			{
				return 0;
			}
		}

		//可能还会有2可以通过,所以不能写0 == map[x1][y1]
		while (1 != map[x1][y1]) //用while(1)个人决定太危险了,因为很可能就死循环了,果然死循环了
		{
			x2 = x1;
			y2 = y1;
			//一直朝着这个方向走
			x1 = x2 + dir[i][0];
			y1 = y2 + dir[i][1];
			if ((x1 <1) || (x1>w) || (y1<1) || (y1>h))
			{
				break;//这个方向不行,得换个方向 出界了还没找到终点
			}		

			if (3 == map[x1][y1]) //到终点了
			{
				if ((ans > (num + 1))&&((num+1)<=10))
				{
					ans = num+1;
					return 1;
				}
				else
				{
					return 0;
				}
			}


			if (1 == map[x1][y1]) //运动中遇到石头,那石头要消失,然后重新选择方向
			{
				map[x1][y1] = 0;
				dfs(x2,y2,0,num+1);
				map[x1][y1] = 1;//回溯,选另一条路
			}
		}		
	}
	return 0;
}

int main()
{
	int i = 0;
	int j = 0;
	freopen("input.txt","r",stdin);
	while (2 == scanf("%d %d", &h, &w) && (0 != h) && (0 != w))
	{
		init();
		for (i = 1; i <= w;i++)
		{
			for (j = 1; j <= h; j++)
			{
				scanf("%d",&map[i][j]);
				if (2 == map[i][j])
				{
					startx = i;
					starty = j;
				}

				if (3 == map[i][j])
				{
					endx = i;
					endy = j;
				}
			}
		}

		dfs(startx, starty,0, 0);

		if (MAXANS != ans)
		{
			printf("%d\n",ans);
		}
		else
		{
			printf("-1\n");
		}
	}
	return 0;
}


 


                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值