poj 3985 bfs+hash+剪枝

3 篇文章 0 订阅

Knight's Problem

Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 1266 Accepted: 237

Description

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

这个题输入的点的宽度有1w,如果直接用一个二维vis数组存访问过的点,果断mle;

那么这里就要用hash来存访问过的点,并且这个hash要是一个无损hash的话至少要开1w*1*w的数组还是要mle;

所以这里就要用到有损hash;

另外,剪枝技巧上,第一点如果这点在起点和终点的反向肯定是要减掉的,这里实际上使用三角形的余弦定理,当那个角为钝角时可以得到一个大小关系;

第二点如果这个点到起点和终点连线得距离,比他一步所能走得最大距离要大,说明这个点实际是走弯了,要剪掉;

另外就是这个题得坑点起点和终点得在-5k——5k,并不是地图得宽就是这么多

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<set>
#include<cmath>
#define sqr(x) ((x) * (x))
using namespace std;
const int MOD=999997;
const int INF=0x3f3f3f3f;
const int maxn=5e5+5;
struct point
{
    int x,y;
    int st;
};
struct node //构造有损哈希
{
    int x,y;
    int nxt;
}edge[maxn];
int has[MOD];//存hash值
int index;
double A,B,C;//直线方程一般式系数
int get_hash(int x,int y)
{
    return (((x<<15)^y)%MOD+MOD)%MOD;
}
bool addedge(int key,int x,int y)
{
    //如果一个哈希值没出现过,has数组值为-1,将它添加到edge中
    //如果出现过,可能是hash值计算重复了,也可能是这个点重复出现了
    //当顺这这个点的hash表往前找发现x,y与原来均相同,说明就是重复点返回flase
    for(int i=has[key];i!=-1;i=edge[i].nxt)
    {
        if(edge[i].x==x&&edge[i].y==y) return false;//这个点出现过
    }
    edge[index].x=x;
    edge[index].y=y;
    edge[index].nxt=has[key];//nxt其实是前一个
    has[key]=index++;
    return true;
}
point  s,e;
double S,T;
int DIS;
int dis(int a,int b,int x,int y)
{
    return  sqr(x-a)+sqr(y-b);
}
bool check(int xx,int yy)//剪枝
{
   // if(xx<=-5000||xx>=5000||yy<=-5000||yy>=5000) return false;
    double cc=fabs(A*xx+B*yy+C*1.0)/T;
    int aa=dis(xx,yy,s.x,s.y);
    int bb=dis(xx,yy,e.x,e.y);
    if(aa+DIS<bb||bb+DIS<aa) return false;
    if(cc<=S) return true;
    return false;
}
int m;
point data[20];
int bfs()
{
    queue<point> q;
    q.push(s);
    addedge(get_hash(s.x,s.y),s.x,s.y);
    while(!q.empty())
    {
        point tt=q.front();
        q.pop();
        if(tt.x==e.x&&tt.y==e.y)
        {
            return tt.st;
        }
        for(int i=0;i<m;++i)
        {
            int xx=tt.x+data[i].x;
            int yy=tt.y+data[i].y;
            point hh;
            hh.st=0;
            hh.x=xx;
            hh.y=yy;
            if(check(xx,yy)&&addedge(get_hash(xx,yy),xx,yy))
            {
                //cout<<xx<<' '<<yy<<endl;
                hh.st=tt.st+1;
                q.push(hh);
            }
        }
    }
    return -1;
}
int tes;
int main()
{
    cin>>tes;
    while(tes--)
    {
        index=0;
        memset(has,-1,sizeof has);
        scanf("%d%d%d%d",&s.x,&s.y,&e.x,&e.y);
        A=s.y-e.y;
        B=e.x-s.x;
        C=s.x*e.y-e.x*s.y;
        DIS=dis(s.x,s.y,e.x,e.y);
        T=sqrt(sqr(A)+sqr(B));
        s.st=0;
        S=-INF;
        scanf("%d",&m);
        for(int i=0;i<m;++i)
            {
                scanf("%d%d",&data[i].x,&data[i].y);
                S=max(S,sqrt(data[i].x*data[i].x+data[i].y*data[i].y));//一步所走的最大距离
            }
        //cout<<S<<endl;
        int ans=bfs();
        if(ans==-1) puts("IMPOSSIBLE");
        else cout<<ans<<endl;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值