hdu1043 A*算法 八数码问题

题意大致就是让你复原一个八数码拼图,输出具体路径

思路:这里使用的是A*算法,A*算法可能比较陌生,迪杰斯克拉法求最短路相比大家都比较熟悉,A*算法就是他的进化,或者说dij算法是A*算法中把预估函数看为0得到的算法,

A*算法的主体是一个路径计算函数:f=g+h,其中g表示的从起始点到某一点的耗费距离,h则表示的是从当前点到目标点的预估耗费,一般在搜图中使用曼哈顿距离,而在本题中则是计算八数码中每个点复原所耗费的最小距离。

然后就跟bfs一样的模板了

代码:

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

using namespace std;

const int maxn = 4e5 + 10;
struct node
{
		int f[3][3];
		int x, y;
		int h, g;
		int hashnum;
		bool operator <(const node a)const
		{
				return g + h > a.g + a.h;
		}
};
struct path
{
		int pre;
		char c;
}pre[maxn];
int ha[9] = {40320,5040,720,120,24,6,2,1,1};
int dir[4][2] = { {-1,0},{1,0},{0,-1},{0,1} };
char d[10] = "udlr";
int vis[maxn];

void print(int x)//因为你是先找,所以要先倒溯到起始点然后输出路径
{
		if (pre[x].pre == -1)  return;
		print(pre[x].pre);
		printf("%c", pre[x].c);
}

int gethash(node e)//利用康拓展卡来进行哈希表。
{
		int a[9];
		int cnt = 0, k = 0;;
		int ans = 0;
		for (int i = 0; i < 3; i++)
				for (int j = 0; j < 3; j++)
						a[cnt++] = e.f[i][j];
		for (int i = 0; i < 9; i++)
		{
				k = 0;
				for (int j = i + 1; j < 9; j++)
				{
						if (a[i] > a[j])   k++;
				}
				ans += k * ha[i];
		}
		return ans;
}

int geth(node e)//计算A*算法中的h函数
{
		int ans = 0;
		for (int i = 0; i < 3; i++)
		{
				for (int j = 0; j < 3; j++)
				{
						if(e.f[i][j])
								ans += abs(i - (e.f[i][j] - 1) / 3) + abs(j - (e.f[i][j] - 1) % 3);
				}
		}
	return ans;
}

void astar(node e)
{
		int t = 0;
		memset(vis, 0, sizeof(vis));
		node a;
		int cnt = 1, xx, yy;
		for (int i = 0; i < 9; i++)
				a.f[i / 3][i % 3] = (i + 1) % 9;
		int end = gethash(a);
		e.hashnum = gethash(e);
		e.g = 0, e.h = geth(e);
		vis[e.hashnum] = 1;
		pre[e.hashnum].pre = -1;
		if (e.hashnum == end)
		{
				printf("\n");
				return;
		}
		priority_queue<node>que;
		que.push(e);
        while(!que.empty())
        {
            e=que.top();
            que.pop();
            for(int i=0;i<4;i++)
            {
                xx=e.x+dir[i][0];
                yy=e.y+dir[i][1];
                if(xx<0||yy<0||xx>=3||yy>=3)continue;
                a=e;
                swap(a.f[e.x][e.y],a.f[xx][yy]);
                int k=gethash(a);
                if(vis[k])continue;
                vis[k]=1;
                a.hashnum=k;
                a.x=xx;
                a.y=yy;
                a.g++;
                a.h=geth(a);
                pre[k].pre=e.hashnum;
                pre[k].c=d[i];
                if(k==end)
                {
                    print(k);
                    printf("\n");
                    return ;
                }
                que.push(a);
            }
        }
}

int main()
{
		string a;
		while (getline(cin, a))
		{
				node e;
				int n = a.size();
				for (int i = 0, j = 0; i < n; i++)
				{
						if (a[i] == ' ')   continue;
						if (a[i] == 'x')
						{
							e.x = j / 3;
                            e.y = j % 3;
							e.f[j / 3][j % 3] = 0;
						}
						else e.f[j / 3][j % 3] = a[i] - '0';
						j++;
				}
				int k = 0;
				for (int i = 0; i < 9; i++)//计算这个八数码中的逆序数,如果逆序数为奇数则判定为不可解决,(从题意中可得出)
				{
						if (e.f[i/3][i%3] == 0)  continue;
						for (int j = 0; j < i; j++)
						{
								if (e.f[j/3][j%3] == 0)  continue;
								if (e.f[j/3][j%3] > e.f[i/3][i%3])    k++;
						}
				}
				if (k & 1)
						printf("unsolvable\n");
				else
						astar(e);
				//a.clear();
		}
		//system("system");
		return 0;
}

 

include using namespace std; struct node{ int nodesun[4][4]; int pre; //上一步在队列中的位置 int flag ; //步数标识,表示当前的步数为有效的 int value; //与目标的差距 int x,y; //空格坐标 }queue[1000]; //移动方向数组 int zx[4]={-1,0,1,0}; int zy[4]={0,-1,0,1}; //当前步数 int top; int desti[4][4];//目标状态 int detect(struct node *p)//检查是否找到 {int i,j; for(i=1;i<4;i++) for(j=1;jnodesun[i][j]!=desti[i][j]) return 0; return 1; } //打印 void printlj() {int tempt; int i,j; tempt=top; while(tempt!=0) { for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<queue[tempt].nodesun[i][j]; if(j==3) cout<<" "<<endl; } tempt=queue[tempt].pre; } } //现在状态与目标状态有多少个不同位置 int VALUE(struct node *p) {int count=0; int i,j; for(i=1;i<4;i++) for(j=1;jnodesun[i][j]!=desti[i][j]) count++; return count; } void main() { //初始化 int i,j,m,n,f; int min=10; int temp,find=0,minnumber; top=1; for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<"请输入第"<<i<<"行"<<"第"<<j<<"列的值"<>temp; queue[1].nodesun[i][j]=temp; } cout<<"请输入初始状态的空格的位置(行)"<>temp; queue[1].x=temp; cout<<"请输入初始状态的空格的位置(列)"<>temp; queue[1].y=temp; queue[1].value=VALUE(&queue[1]); queue[1].pre=0; //上一步在队列中的位置 queue[1].flag=0; //目标状态 for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<"请输入目标状态第"<<i<<"行"<<"第"<<j<<"列的值"<>temp; desti[i][j]=temp; } //根据估价函数 while(!find&&top>0) { for(i=1;i<=top;i++) //////////////////////////////////////////// //min为上一图中与目标图有多少个元素不相同,queue[i]为当前图与目标图有多少个元素不相同通过这两个数的比较,就可以得出当前图较之上一图向目标图接近同时把当前的i记录下来进行下一步比较 {if(queue[i].value<min&&queue[i].flag==0) {minnumber=i;// min=queue[i].value; //还有多少不同的位数 } } queue[minnumber].flag=1; //表示此位有效 ////////////////////////////////////// // for(f=0;f=1&&i=1&&j<=3) {top++; ///////////////////////////////////////////// //位置交换 queue[top]=queue[minnumber]; queue[top].nodesun[m][n]=queue[minnumber].nodesun[i][j]; queue[top].nodesun[i][j]=0; /////////////////////////////////////// //空格移动方向 queue[top].x=i; queue[top].y=j; /////////////////////////////////////// queue[top].pre=minnumber; //上一步在队列中的位置 queue[top].value=VALUE(&queue[top]); //有多少位与目标不同 queue[top].flag=0; //标识位初始化 if(detect(&queue[top])) //检查是否为目标 {printlj(); //打印 find=1; //设找到标识位 break; } } } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值