肉骨头的诱惑

肉骨头的诱惑

题目描述

小狗在一个古老的迷宫中发现了一根骨头,这使他非常着迷。但是,当他捡起它时,迷宫开始摇晃,小狗可以感觉
到地面下沉。他意识到骨头是一个陷阱,他拼命试图摆脱这个迷宫。迷宫是一个矩形,大小为N×M。迷宫中有一扇
门。刚开始时,门是关闭的,它将在第T秒打开一小段时间(少于1秒)。因此,小狗必须在第T秒精确到达门。每
秒钟,他可以将一个块移动到上,下,左和右相邻的块之一。一旦他进入一个块,该块的地面将开始下沉并在下一
秒消失。他不能在一个块停留超过一秒钟,也不能走到曾经走过的块区上。可怜的小狗可以生存吗?请帮助他。

输入

输入包含多个测试用例。每个测试用例的第一行包含三个整数N,M和T(1 <N,M <= 7; 0 <T <50),表示迷宫
的大小和门打开的时间,分别。接下来的N行给出迷宫的布局,每行包含M个字符。角色是以下字符之一:'X':小
狗无法进入的墙块;'S':小狗的起点;'D':门;或“.”:空白块。输入以三个0终止。该测试用例将不被处理。

输出

对于每个测试用例,如果小狗可以存活,则在一行中输出“YES”,否则输出“NO”。

样例输入 Copy

4 4 5
S.X.
..X.
..XD
....
3 4 5
S.X.
..X.
...D
0 0 0

样例输出 Copy

NO
YES

这道题我错了很多个TLE

老师给我发了题解——虽然还是TLE50。 

题解如下

(Code中的Map在最后的Code中修改[Map会出问题])

肉骨头的诱惑

奇偶剪枝:根据题目,dog必须在第t秒到达门口。也就是需要走t-1步。设dog开始的位置为(sx,sy),目标位置为(ex,ey).如果abs(ex-x)+abs(ey-y)为偶数,则abs(ex-x)和abs(ey-y)奇偶性相同,所以需要走偶数步;?
当abs(ex-x)+abs(ey-y)为奇数时,则abs(ex-x)和abs(ey-y)奇偶性不同,到达目标位置就需要走奇数步。先判断奇偶性再搜索可以节省很多时间,不然的话容易超时。t-sum为到达目标位置还需要多少步。因为题目要求doggie必须在第t秒到达门的位置,所以(t-step)和abs(ex-x)+abs(ey-y)的奇偶性必然相同。因此temp=(t-step)-abs(ex-x)+abs(ey-y)必然为偶数


1.1010 temp of the bone 
2. 
3.sample input:
4.4 4 5
5.S.X.
6...X.
7...XD
8.....
9. 
10.问题:
11.(1):
12.在发现当前节点无法到达时,这点弹出栈,并且把这点的标记重新刷为'.'
13. 
14.(2):
15.如何在dfs中既要保证到达又要使时间正好呢?? 在函数中通过这种形式实现:
16. 
17.dfs(int si,int sj,int cnt) 就是用cnt来记录当时的时间,并且在
18. 
19.if( si==di && sj==dj && cnt==t )
20.    {
21.        escape = 1;
22.        return;
23.    }
24. 
25.的时候 即当前点到达了终点并且时间恰好等于题目所给限制时间时,跳出
26.并且escape标记为真
27. 
28.(3):
29.如何让一个点有顺序地遍历它四周地能到达的点呢??
30.聪明并且简短的方法是设施一个dir[4][2] 数组 控制方向
31.并且设置它的值为dir[4][2]={{0,-1},{0,1},{1,0},{-1,0}};
32. 
33.遍历的时候用for(i:0->4)就非常方便了
34. 
35.(4):
36.千万要注意的是节点越界的情况, dfs(int si,int sj,int cnt)的时候一定要把 si, sj 控制在给你的矩阵内 在后面会提到一个我的列子 就是因为访问了[0, -1]的位置导致了其
37. 
38.他数据被更改
39. 
40.(5):
41.读入矩阵的时候,可以采用for(i = 1; i <= N; i++)
42.               for(j = 1; j <= M; j++)
43.                scanf("%c", &map[i][j]);        
44. 
45.的方法,好处在于可以控制和计算每一个读入的数据,坏处是调试的时候对矩阵的观察不太方便,而且好像还会有错误,在2102"A计划"用这种方法读入数据时好像就会wa,
46. 
47.另一种方法是for(i = 0; i < N; i++) gets(map[i]);
48.这样读入的数据在调试观察的时候十分方便 gets()读入的默认为字符串,在vc调试的时候是显式的 可以直接观察矩阵 缺点是对矩阵中各个数据的计算和控制无法实现,需要读完后再遍历一遍
49. 
50.(6)
51.能用bfs还是尽量用bfs 我不会bfs.... dfs的递归在调试的时候不是很方便,而且bfs要比dfs快,调试也要方便,因为它没有递归
52. 
53.(7)
54.关于剪枝,没有剪枝的搜索不太可能,这题上课的时候讲过两个剪枝,一个是奇偶剪枝,一个是路径剪枝
55. 
56.奇偶剪枝:
57.把矩阵标记成如下形式:
58.0,1,0,1,0
59.1,0,1,0,1
60.0,1,0,1,0
61.1,0,1,0,1
62.很明显,如果起点在0 而终点在1 那显然 要经过奇数步才能从起点走到终点,依次类推,奇偶相同的偶数步,奇偶不同的奇数步
63.在读入数据的时候就可以判断,并且做剪枝,当然做的时候并不要求把整个矩阵0,1刷一遍,读入的时候起点记为(Si,Sj) 终点记为(Di,Dj) 判断(Si+Sj) 和 (Di+Dj) 的奇偶性就可以了
64. 
65.路径剪枝:
66.矩阵的大小是N*M 墙的数量记为wall 如果能走的路的数量 N*M - wall 小于时间T,就是说走完也不能到总的时间的,这显然是错误的,可以直接跳出了
67. 
68.课件里面给过这题的标程,在dfs的过程中有个没提到的剪枝,就是记录当前点到终点的最短路,如果小于剩余的时间的话,就跳出
69.这个剪枝我觉得更科学,它毕竟是动态的么,标程里面是这么写的:
70.temp = (t-cnt) - abs(si-di) - abs(sj-dj);
71.if( temp<0 || temp&1 ) return;
72. 
73.其中求当前点到终点的最短路是这样 abs(si-di) - abs(sj-dj) 这个就比较粗糙了 明显没有考虑到碰到墙要拐弯的情况
74.那求最短路有没有什么好办法呢?
75. 
76.我曾经想到过用 Dijkstraq求最短路的 ,明显大才小用,在论坛里看到一个方法觉得可以用在这里
77.给定下例:
78. 
79.S.X.
80...X.
81...XD
82.....
83. 
84.每个点到终点的最短路是不是这样呢:
85. 
86.S6X2
87.65X1
88.54XD
89.4321
90. 
91.这怎么求呢??从终点开始遍历整个数组,终点是0,它周围的点都+1,墙就不计数,依次类推,就能求得这个矩阵的一个最短时间矩阵,在dfs的时候比较当前点到终点的最短路,如果大于剩余时间的话就跳出
92. 
93.这个方法的预处理还是非常快的,我没有用过,但是感觉会非常有用处.
94. 
95.(8)
96.在做这题的时候,我碰到过一个神奇的事情,在程序运行至下面代码时
97. 
98.if( map[ si+dir[i][0] ][ sj+dir[i][1] ] != 'X')            
99.    map[ si+dir[i][0] ][ sj+dir[i][1] ] = 'X';
100. 
101.T被改变了!! 这丝毫和T没有关系啊,怎么改变T的值呢??
102. 
103.原来在起点map[0][0]进入时,我没有注意到map[ si+dir[i][0] ][ sj+dir[i][1] ] 实际做的是map[0][-1] = 'X'; 很危险的一个赋值,书本上千万次强调的东西让我碰上了,这个地方我找了很久,因此我觉得有必要单独列出来提醒自己
104. 
105.//////////////////////////////////////////////////////////////////////////////////////////////////////////////
106.下面我把一个带注释的标程贴一下,不是我写的注释
107. 
108. 
109.//zju 2110 Tempter of the Bone
110.这个注释当初还是帮了我很大忙的,起码让我看懂了课件
111. 

题解中的Code我整理了一下

源代码:

#include<bits/stdc++.h>
using namespace std;
char p[9][9];
int n,m,t,c,d,dir[4][2]= {{0,-1},{0,1},{1,0},{-1,0}},i,j,a,b;
bool q;
void dfs(int a,int b,int cnt) {
	int i,temp;
	if(a>n||b>m||a<=0||b<=0) return;
	if(a==c&&b==d&&cnt==t) {
		q=1;
		return;
	}
	temp=(t-cnt)-abs(a-c)-abs(b-d);
	if(temp<0||temp&1) return;
	for(i=0; i<4; i++) {
		if(p[a+dir[i][0]][b+dir[i][1]]!='X') {
			p[a+dir[i][0]][b+dir[i][1]]='X';
			dfs(a+dir[i][0], b+dir[i][1], cnt+1);
			if(q) return;
			p[a+dir[i][0]][b+dir[i][1]]='.';
		}
	}
	return;
}
int main() {
	while(cin>>n>>m>>t) {
		if(!n&&!m&&!t) break;
		int wall=0;
		for(i=1; i<=n; i++ )
			for(j=1; j<=m; j++ ) {
				cin>>p[i][j];
				if(p[i][j]=='S') {
					a=i;
					b=j;
				} else if(p[i][j]=='D') {
					c=i;
					d=j;
				} else if(p[i][j]=='X') wall++;
			}
		if(n*m-wall<=t) {
			cout<<"NO"<<endl;
			continue;
		}
		q=0;
		p[a][b]='X';
		dfs(a,b,0);
		if(q) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
	return 0;
}

本Code——TLE(50分)

求Orz指点,虽已剪枝,可仍为什么还是错的 

AC

猜你喜欢

转载自blog.csdn.net/m0_45682806/article/details/107795707