Day2-I-Knight's Problem POJ - 3985

You must have heard of the Knight's Tour problem. In that problem, a knight is placed on an empty chess board and you are to determine whether it can visit each square on the board exactly once. 

Let's consider a variation of the knight's tour problem. In this problem, a knight is place on an infinite plane and it's restricted to make certain moves. For example, it may be placed at (0, 0) and can make two kinds of moves: Denote its current place as (x,y), it can only move to (x+1,y+2) or (x+2,y+1). The goal of this problem is to make the knight reach a destination position as quickly as possible (i.e. make as less moves as possible).

Input

The first line contains an integer T ( T < 20) indicating the number of test cases. 
Each test case begins with a line containing four integer: fx fy tx ty(-5000<=fx,fy,tx,ty<=5000). The knight is originally placed at (fx, fy) and (tx, ty) is its destination. 

The following line contains an integer m(0 < m <= 10), indicating how many kinds of moves the knight can make. 

Each of the following m lines contains two integer mx my(-10<=mx,my<=10; |mx|+|my|>0), which means that if a knight stands at (x,y), it can move to (x+mx,y+my).

Output

Output one line for each test case. It contains an integer indicating the least number of moves needed for the knight to reach its destination. Output "IMPOSSIBLE" if the knight may never gets to its target position.

Sample Input

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

Sample Output

3
IMPOSSIBLE

简述:给你起点和终点以及m种移动方式,问是否能够达到终点,若能输出最短步数。
分析:这题乍一看是一道简单的BFS,但是他没有限制搜索“棋盘”的大小,直接裸会T,那么我们就要进行合理剪枝,去除一些不可能的情况。
1.若该点是背离起点/终点的,应剪枝:
  这点应该比较好理解,背离的路径一定不会是最短路径,用余弦定理即可判断。
但是,如果最短路径是要先背离再回来呢?我们先引入一个结论:改变路径的顺序不会影响最终到达终点,以图为例:

这样,就引出了我们的第二种剪枝:

2.每一步必须在最大距离之内:

既然可以随意转换步数,那么我们就可以将每一步限制在最大距离之内,这样就可以将无穷距离进行限制,转换为有限的,并且也能将第一种剪枝无法判断的情况(未背离但是不会达到)給剪掉。

PS:计算距离的时候用的是点到直线的距离公式(梦回高中

这题还有一点,要手写一下hash,用STL的会T,参考黑书(数据结构与算法分析)上的分离链接法。

(有看不懂的欢迎留言,文学功底有限。。)

代码如下:

#define sqr(x) ((x) * (x))
const int prime = 999997;

int T, sx, sy, ex, ey, n, order[15][2], head[prime], idx, length;
double A, B, C, d; // Ax+By+C=0

struct Node {
    int x, y, step;
};

struct unit {
    int x, y, next;
} edge[400000];

int Hash(int x,int y) {
    return (((x << 13) ^ y) % prime + prime) % prime; // 防止负数
}

bool addedge(int key,int x,int y) {
    for (int i = head[key]; i != -1; i = edge[i].next) {
        if(edge[i].x == x && edge[i].y == y)
            return false;
    }
    edge[idx].x = x, edge[idx].y = y;
    edge[idx].next = head[key];
    head[key] = idx++;
    return true;
}

bool check(int x,int y) {
    int t1 = sqr(x - sx) + sqr(y - sy);
    int t2 = sqr(ex - x) + sqr(ey - y);
    double t3 = sqr(A * x + B * y + C) * 1.0 / ((sqr(A) + sqr(B)) * 1.0);
    if(t2 > t1 + length || t1 > t2 + length)   // 情况1
        return false;
    if(t3 <= d)
        return true;    // 情况2
    return false;
}

bool bfs() {
    queue<Node> q;
    Node p, tmp;
    p.x = sx, p.y = sy, p.step = 0;
    q.push(p);
    addedge(Hash(sx, sy), sx, sy);
    while(!q.empty()) {
        p = q.front(), q.pop();
        if(p.x == ex && p.y == ey) {
            printf("%d\n", p.step);
            return true;
        }
        for (int i = 0; i < n; ++i) {
            tmp = p;
            tmp.x += order[i][0], tmp.y += order[i][1];
            if(check(tmp.x,tmp.y)&&addedge(Hash(tmp.x,tmp.y),tmp.x,tmp.y)) {
                tmp.step++;
                q.push(tmp);
            }
        }
    }
    return false;
}

int main() {
    scanf("%d", &T);
    while(T--) {
        d = 0, idx = 0;
        scanf("%d%d%d%d", &sx, &sy, &ex, &ey);
        scanf("%d", &n);
        for (int i = 0; i < n; ++i) {
            scanf("%d%d", &order[i][0], &order[i][1]);
            d = max(d, sqr(order[i][0]) + sqr(order[i][1])*1.0);
        }
        A = ey - sy, B = sx - ex, C = ex * sy - ey * sx;
        length = sqr(ex - sy) + sqr(ey - sy);
        memset(head, -1, sizeof(head));
        if(!bfs())
            printf("IMPOSSIBLE\n");
    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/GRedComeT/p/11228396.html

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值