POJ 3985 Knight's Problem bfs + 剪枝

题意:在无限大的棋盘上给定起点和终点的坐标,然后给定n(0<=n<=10)个向量,问从起点走到终点最小的步数是多少。

题解:bfs是肯定的,但是裸的bfs会T,可以想象任意一个最短步数形成的路径任意交换两个选择的向量形成的路径是不一样的,那么一定能找到在路径中离

          l:(sx,sy)-> (tx,ty)直线最近的路径,所以只要找到这个路径即可,可以将所有背离起点终点和离 l 距离超过最大向量的点不入队,这样搜到

          的点的个数极限就是(5000 + 5000)* sqrt(2)* 10 * 2 * sqrt(2) = 400000,然后写个hash记录即可。

          开始用set< pair<int , int> >判重也会T,卡的好死唉。


Sure原创,转载请注明出处。

#include <iostream>
#include <cstdio>
#include <memory.h>
#include <queue>
#include <set>
#include <utility>
#define MAX(a , b) ((a) > (b) ? (a) : (b))
#define sqr(x) ((x) * (x))
#define mp make_pair
typedef long long ll;
using namespace std;
const int maxn = 12;
const int maxm = 400000;
const int kr = 1;
const int prime = 999997;
struct ddd
{
    int x,y;
    int step;
};
struct node
{
    int x,y;
    int next;
}edge[maxm];
int head[prime],vec[maxn][2];
queue <ddd> Q;
int sx,sy,tx,ty,a,b,c,d,n,idx;

void init()
{
    memset(head,-1,sizeof(head));
    idx = 0;
    return;
}

int hash(int x,int y)
{
    return (((x << 15) ^ 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;
}

void read()
{
    d = 0;
    scanf("%d %d %d %d",&sx,&sy,&tx,&ty);
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d %d",&vec[i][0],&vec[i][1]);
        d = MAX(d , sqr(vec[i][0]) + sqr(vec[i][1]));
    }
    a = ty - sy;
    b = sx - tx;
    c = sy * tx - sx * ty;
    return;
}

bool IsValid(int x,int y)
{
    if(sqr(x - sx) + sqr(y - sy) <= sqr(kr) * d) return true;
    if(sqr(x - tx) + sqr(y - ty) <= sqr(kr) * d) return true;
    if((tx - sx) * (x - sx) + (ty - sy) * (y - sy) < 0) return false;
    if((sx - tx) * (x - tx) + (sy - ty) * (y - ty) < 0) return false;
    if(sqr(ll(a) * x + b * y + c) <= ll(d) * (sqr(a) + sqr(b))) return true;
    return false;
}

void bfs()
{
    while(!Q.empty()) Q.pop();
    ddd cur,tmp;
    tmp.x = sx;
    tmp.y = sy;
    tmp.step = 0;
    Q.push(tmp);
    addedge(hash(sx , sy) , sx , sy);
    while(!Q.empty())
    {
        cur = Q.front();
        Q.pop();
        if(cur.x == tx && cur.y == ty)
        {
            printf("%d\n",cur.step);
            return;
        }
        for(int i=0;i<n;i++)
        {
            int xx = cur.x + vec[i][0];
            int yy = cur.y + vec[i][1];
            if(IsValid(xx , yy) && addedge(hash(xx , yy) , xx , yy))
            {
                tmp.x = xx;
                tmp.y = yy;
                tmp.step = cur.step + 1;
                Q.push(tmp);
            }
        }
    }
    puts("IMPOSSIBLE");
    return;
}

int main()
{
    int cas;
    scanf("%d",&cas);
    while(cas--)
    {
        init();
        read();
        bfs();
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值