hdu1043 A*算法 八数码问题

版权声明:那个,最起码帮我加点人气吧,署个名总行吧 https://blog.csdn.net/qq_41670466/article/details/84316948

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

思路:这里使用的是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;
}

猜你喜欢

转载自blog.csdn.net/qq_41670466/article/details/84316948