1. 程式人生 > >hdu1043 A*演算法 八數碼問題

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;
}