POJ 1324 Holedox Moving 状态压缩+A* -

题目地址:http://poj.org/problem?id=1324

写了一天了 ,一开始想表示状态用头和尾位置,但很快写出来就WR

然后就用根据贪吃蛇的节点与上一个节点位置关系保存状态,一共有4个状态,上下左右,0~3表示,正好二进制占两个位

最多有14个状态,所以int就可以保存

然后写出来后MLE然后TLE然后MLE然后TLE然后WR.... 不容易啊woc...

最后发现vector太慢了,果断用数组,再重写一遍

然后发现h()定义有很大问题,再改

还有不同h()计算方式不行,h()一定要体现h()+g()最小离终点最近,这样才会正确

所以单单把h()*100是不行的,因为有种可能会导致h()小但g()很大的排在队列前面

h()定义法则如下:


我选的是以头节点离终点的曼哈顿距离为h(), 这样肯定相容


代码如下:23864K 1672MS

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxn=20+5;
struct Node{
	int x,y,snack,f,g,h;
	Node(int x=0,int y=0,int s=0,int g=0,int h=0):x(x),y(y),snack(s),f(g+h),g(g),h(h){}
	bool operator < (const Node& n) const {
		if(f==n.f) return g>n.g;
		return f>n.f;
	}
};
struct Snack{
	int x[10],y[10];
};
bool closed[maxn][maxn][1<<15]; 
int R,C,m;
bool G[maxn][maxn];
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};

int H(const Snack& s){
	int cnt=0;
//	for(int i=0;i<m;i++)
//		cnt+=(s.x[i]+s.y[i])*i*100; //头的比重大 
	cnt+=s.x[0]+s.y[0];
	return cnt*100;
}
bool inside(int x,int y){
	return x>=1&&x<=R&&y>=1&&y<=C; 
}
int Encode(const Snack& s){
	int code=0;
	for(int i=m-1;i>=1;i--){
		int xt=find(dx,dx+4,s.x[i]-s.x[i-1])-dx;
		int yt=find(dy,dy+4,s.y[i]-s.y[i-1])-dy;
		int t=max(xt,yt);
		code<<=2; code|=t;
	}
	return code;
}
Snack Decode(int x,int y,int s){
	Snack ns;
	ns.x[0]=x,ns.y[0]=y;
	for(int i=1;i<m;i++){
		int t=3; t&=s;
		ns.x[i]=ns.x[i-1]+dx[t];
		ns.y[i]=ns.y[i-1]+dy[t];
		s>>=2;
	}
	return ns;
}
Node move(Node s,int i){ //i=up down left right
	int x=s.x+dx[i];
	int y=s.y+dy[i];
	int t=i^1;       //反方向 
	int snack=s.snack;
	snack<<=2; snack|=t;   
	snack&=((1<<((m-1)*2))-1);  //去掉最高位 
	int h=H(Decode(x,y,snack));
	return Node(x,y,snack,s.g+1,h);
}
bool bCross(int x,int y,int s,int i){
	int nx=x+dx[i];
	int ny=y+dy[i];
	if(!inside(nx,ny)||G[nx][ny]) return false;
	
	Snack snack=Decode(x,y,s);
	for(int i=1;i<m;i++)
	if(nx==snack.x[i]&&ny==snack.y[i]) return false;
	return true;
}
bool inClosed(Node s){
	if(closed[s.x][s.y][s.snack]) return true;
	closed[s.x][s.y][s.snack]=true;
	return false;
}
int A_star(Node start)
{
	memset(closed,false,sizeof(closed));
	priority_queue<Node> open;
	open.push(start);
	inClosed(start);
	while(!open.empty())
	{
		Node s=open.top(); open.pop();
		if(s.x==1&&s.y==1) return s.g;
		for(int i=0;i<4;i++)
		{
			if(!bCross(s.x,s.y,s.snack,i)) continue; 
			Node ns=move(s,i);
			if(inClosed(ns)) continue;
			else open.push(ns);
		}
	}
	return -1;
} 
int main()
{
	int n,x,y,kase=0; Snack snack;
	while(scanf("%d%d%d",&R,&C,&m)!=EOF)
	{
		if(R==0&&C==0&&m==0) break;
		for(int i=0;i<m;i++){	
			scanf("%d%d",&x,&y);
			snack.x[i]=x;
			snack.y[i]=y;
		}
		scanf("%d",&n);
		memset(G,false,sizeof(G));
		for(int i=0;i<n;i++){
			scanf("%d%d",&x,&y);
			G[x][y]=true;
		}
		Node s=Node(snack.x[0],snack.y[0],Encode(snack),0,H(snack));
		printf("Case %d: %d\n",++kase,A_star(s));
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值