您的位置:首页 > 其它

HDU 1010 dfs 奇偶剪枝

2017-08-22 00:00 253 查看
题意:根据地图,'S'为开始位置,'D'为门的位置,' . '为空地,'X'为墙,不能经过,

问:在指定的时间,是否能到达'门'的位置.

注意:路不可以重复经过,时间也要刚好是 t ,不能少.

思路:还是DFS,不能用BFS,因为BFS求的是最短路径,而此题的路径不一定最短.

剪枝是关键,奇偶剪枝.

奇偶剪枝原理:

要理解奇偶剪枝,先了解一下曼哈顿距离,从一个点到达另外一个点的最短路径长度(时间)可以根据两点坐标求出,

路径长度(非最短)与最短路径的长度同奇偶,它们的差一定是偶数!举个例子,就像两个偶数的差差是偶数,两个个数的差也是偶数.

本题还有一个剪枝:n*m-wall与t的关系,wall为'X'的数量,解释一下,n*m为区域总数,

所以m*n-wall<=t 一定不到到达终点,注意,少时等号在杭电上运行时间为546MS,而加上等号运行时间才为78MS!

eg:

3 3 4

SXX

.XX

X.D

上面的例子满足m*n-wall=t,确实不能到达,但不能找到合理的解释......



奇偶剪枝:

是数据结构的搜索中,剪枝的一种特殊小技巧。

现假设起点为(sx,sy),终点为(ex,ey),给定t步恰好走到终点,

 

s
|
|
|
+e
如图所示(“|”竖走,“—”横走,“+”转弯),易证abs(ex-sx)+abs(ey-sy)为此问题类中任意情况下,起点到终点的最短步数,记做step,此处step1=8;

  

s
+
|+
|
+e
如图,为一般情况下非最短路径的任意走法举例,step2=14;

step2-step1=6,偏移路径为6,偶数(易证);

故,若t-[abs(ex-sx)+abs(ey-sy)]结果为非偶数(奇数),则无法在t步恰好到达;

返回,false;

反之亦反。

70

// http ://acm.hdu.edu.cn/showproblem.php?pid=1010
// 大致题意:给一幅图,有起点有墙有终点,问能不能在刚好t秒的时间走到终点
// DFS + 多重剪枝(奇偶性剪枝)
// 一开始果断DFS,交上去TLE了。。。用了好几重的剪枝才过。。。。
// 所以不要小看剪枝,往往优化个成百上千倍
#include <iostream>
#include <cmath>
using namespace std;
char map[10][10];
int flag, Xnum, Sx, Sy, Dx, Dy;
int n, m, t;
int dir[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};

void DFS(int x, int y, int time){
if (x <= 0 || x > n || y <= 0 || y > m) return;
if (flag == 1) return; //1.马上中断
if (x == Dx && y == Dy && time == t){
if (time == t)
flag = 1;
return;
}

int temp = (t - time) - abs(x - Dx) - abs(y - Dy);//2.奇偶性剪枝
if (temp < 0 || temp & 1) return;

for (int i = 0; i < 4; i++){
int x1 = x + dir[i][0];
7fe8
int y1 = y + dir[i][1];
if (map[x1][y1] != 'X'){
map[x1][y1] = 'X';
DFS(x1, y1, time + 1);
map[x1][y1] = '.';
}
}
return;
}

int main(){
while (cin >> n >> m >> t){
if (n == 0 && m == 0 && t == 0) break;
Xnum = 0;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
cin >> map[i][j];
if (map[i][j] == 'S'){
Sx = i;
Sy = j;
}
if (map[i][j] == 'D'){
Dx = i;
Dy = j;
}
if (map[i][j] == 'X')
Xnum++;
}
}

flag = 0;
map[Sx][Sy] = 'X';

if (n * m - Xnum <= t){//3.提前判断t过大的情况避免再去搜,写上由500MS降到46MS
printf("NO\n");
continue;
}
DFS(Sx, Sy, 0);

printf("%s",flag?"YES\n":"NO\n" );
}
return 0;
}


奇偶剪枝

设起点s的坐标为(sx,sy),终点e的坐标为(ex,ey);

对s的一次操作为对sx或sy进行+1或-1;

若经过t次操作后,s的坐标刚好等于e,则说从s经过t步可以到达e。

在理想情况下,s到e需要的最小步数为m

m=|ex-sx|+|ey-sy|

若t<m,肯定是无法到达的。

当t>=m时,从s到e的行走路径由两部分组成,

一部分为需要走的最少步数m;

另一部分是为了使得刚好行走t步到达e,所需要走的附加步数a,a=t-m;

这a步即为:从需要走m步的最短路径上走出去,再回到最短路径上所走的步数。

假设走出去这段路径长度为b,那回来时的路径长度一定也是b,因此,附加步数的路径长度a等于2b步。

因为走出去时,对坐标进行了b次+1或-1的操作,为使坐标再恢复到最短路径上,就需要进行b次-1或+1的操作,并且与走出去时是相反的。注意:走出去和拐回来的过程中可能参杂着最短路径上的操作,所以b是除去这些参杂操作后的步数。

如下图所示:



从s到e的黑色路径为一条最短路径,红色和蓝色路线组成的路径,为走出最短路径的路径。其中蓝色箭头是参杂着的最短路径中的操作,只有红色箭头才是走出去和拐回来的路径,如果将红色路径去掉,从s向右走,经过绿色箭头到达e,这也是一条最短路径。

因为a=2b,是个偶数,又因为a=t-m,所以当t和m的奇偶性相同时,a才能是偶数。也就是说,当t和m的奇偶性相同时,才有可能从s经过t步,到达e。

所以,当最小步数m与t同为奇数,或同为偶数时,才有可能从s经过t步,到达e

最小距离和绕路距离同奇偶

奇偶剪枝 TLE->500

路径长度 剪枝 500->50

50

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>

#define LL long long
int const MAX = 1e6 + 1;
int const INF = 1 << 30;
double const EPS = 0.00000001;
using namespace std;

int n, m, p, xnum;
int g[10][10];
int stx, sty, edx, edy;
int dir[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};
bool flag;
char s[11];
void dfs(int x, int y, int step){
if (step > p || flag == 1) return;
if (x == edx && y == edy && step == p){
flag = 1;
return;
}

int mindis = abs(x - edx) + abs(y - edy);
//步数不够,或者奇偶性不对
if (mindis > p - step || ((mindis + p - step) & 1))
return;
for (int i = 0; i < 4; i++){
int nx = x + dir[i][0], ny = y + dir[i][1];
if (g[nx][ny]){
g[nx][ny] = 0;
dfs(nx, ny, step + 1);
g[nx][ny] = 1;
}
}
}
int main(){
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);

while (scanf("%d%d%d", &n, &m, &p) == 3 && n + m + p){
xnum = 0;
flag = 0;
memset(g, 0, sizeof(g));
for (int i = 1; i <= n; i++){
scanf("%s", s);
for (int j = 1; j <= m; j++){
g[i][j] = (s[j - 1] == 'X' ? 0 : 1);
if (s[j - 1] == 'S')
stx = i, sty = j;
if (s[j - 1 ] == 'D')
edx = i, edy = j;
if (s[j - 1] == 'X')
xnum++;
}
}

// for (int i = 0; i < n; i++)
//     printf("%s\n", g[i]);
// g[0]表示不可以走
g[stx][sty] = 0;
//最小步数不够到达目标点,走n步,需要n+1块可以走的格子
if (xnum >= (n * m) - p)
printf("NO\n");
else {
dfs(stx, sty, 0);
printf("%s", flag ? "YES\n" : "NO\n");
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dfs 剪枝 曼哈顿距离