IDAstar搜索

http://poj.org/problem?id=1077

Source Code

Problem: 1077  User: p_hoenix 
Memory: 220K  Time: 0MS 
Language: C++  Result: Accepted 

Source Code 
#include <iostream>
#include <utility>
#include <vector>
#include <fstream>
#include <math.h>
using namespace std;

const int MAX = 1 << 20;
const int ROW = 3, COL = 3;



//棋盘
struct Board
{
	Board& operator=(const Board &b)
	{
		for(int i = 0; i < ROW; i++)
			for(int j = 0; j < COL; j++)
				tile[i][j] = b.tile[i][j];
		r = b.r;
		c = b.c;
		return *this;
	}
	int tile[ROW][COL];//4*4的方块
	int r,c;//空格的位置
};
typedef Board Status;

struct Step
{
	int r,c;
	int dir;
};

class PicturePizzle
{
public:
	friend void input(PicturePizzle &pp);
	
	PicturePizzle()
	{
		pStepDir = (int*)malloc(sizeof(int) * MAX);
		stepHeap = (Board*)malloc(sizeof(Board) * MAX);
	}
	
	~PicturePizzle()
	{
		free(pStepDir);
		free(stepHeap);
	}
	//移动步骤.
	typedef struct{
		int r,c;//空格位置
		int dir;//移动方向
	}Step;
	
	//判断以该状态开始是否可以完成拼图
	bool JudgeValidStatus();
	//heuristic function,启发式函数,计算(估算)最大移动步数
	int Heuristic();
	//IDA*搜索寻找移动方案
	//当前状态,深度,h(),前一步的移动方向.
	bool IDAstar(Status &s, int depth, int h, int prevDir);
	
	//打印移动路径
	void ShowStep(int depth);
	
	void Run();
	
	//private:
	Board board;
	int h;
	int maxDepth;
	vector<Board> step;
	int *pStepDir;
	Board *stepHeap;
	//郁闷的地方,C++ primer里面说可以给const static在里面初始化,在类外面定义的,但是VC6.0不可以
	//目标棋盘
	const static Board destBoard;// = {1 ,2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 4, 4};
	const static int nDestPos[ROW * COL + 1][2];//最终棋盘的各个方块的位置
	const static int dir[4][2];
};

//1到16的方块编号
const Board PicturePizzle::destBoard = {1 ,2, 3, 4, 5, 6, 7, 8, 9, 2, 2};//10, 11, 12, 13, 14, 15, 16, 3, 3};
const int PicturePizzle::nDestPos[ROW * COL + 1][2] = {{-1, -1},
{0, 0}, {0, 1}, {0, 2},
{1, 0}, {1, 1},{1, 2},
{2, 0}, {2, 1}, {2, 2}};
//{3, 0}, {3, 1}, {3, 2}, {3, 3}};

const int PicturePizzle::dir[4][2] = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}};//左上右下

bool PicturePizzle::JudgeValidStatus()
{
	int i, j, array[ROW * COL], k, sum;
	sum = k = 0;
	//转化成一维数组
	for(i = 0; i < ROW; i++)
		for(j = 0; j < COL; j++)
		{
			array[k++] = board.tile[i][j];
		}
	//因为在最终状态下,棋盘的一维数组是有序递增的.
	//除了X结点外,要到达目标位置需要移动的最大步数,因为在转换成了一维后原本可以上下移动的都被转化成了水平移动.
	//但是幸运的是,他们是同奇同偶的.具体的证明我也不知道,画图有这么个规律
	for(i = 0; i <= ROW * COL - 2; i++)
		for(j = i + 1; j <= ROW * COL - 1; j++)
			if(array[i] > array[j])
				sum++;
	//这个是X方块到目标位置的最短步数,不管怎么移动,只要最后是在目标位置,必定是同奇同偶的.(简单的考虑就是撤销)
	//而每一次的其他方块的移动都是与X方块的交换来实现的,所以他们的和必定是偶数
	sum += abs(board.r - nDestPos[ROW * COL][0]) + abs(board.c - nDestPos[ROW * COL][1]);
	return sum % 2 == 0;
}

int PicturePizzle::Heuristic()
{
	int r, c, tile, sum = 0;
	for(r = 0; r < ROW; r++)
		for(c = 0; c < COL; c++)
		{
			tile = board.tile[r][c];
			//计算每个方块(非空格)移动到目标位置的曼哈顿距离,不需要计算空格
			if(tile != ROW * COL)
			{
				sum += abs(r - nDestPos[tile][0]) + abs(c - nDestPos[tile][1]);
			}
		}
	h = sum;
	return sum;
}



int global = 0;
bool PicturePizzle::IDAstar(Status &s, int depth, int h, int prevDir)
{
	//cout<<global++<<endl;
	
	if(memcmp(&s, &destBoard, sizeof(destBoard)) == 0)//到达目标状态
	{
		this->ShowStep(depth);
		return true;
	}
	if(depth >= maxDepth)//超过当前深度
		return false;
	Status ns;
	int nr,nc,nh,moveTile;
	for(int d = 0; d < 4; d++)
	{
		if(abs(d - prevDir) == 2)//会撤销上次的移动
			continue;
		ns = s;
		nr = s.r + dir[d][0];
		nc = s.c + dir[d][1];
		if(nr < 0 || nr > ROW - 1 || nc < 0 || nc > COL - 1)//越界
			continue;
		//移动方块
		moveTile = s.tile[nr][nc];
		ns.tile[s.r][s.c] = moveTile;
		ns.tile[nr][nc] = ROW * COL;
		ns.r = nr;
		ns.c = nc;
		
		//向左移动.并且与X交换的方块离它的目标位置近了一步.
		//反过来,因为X是向左的,那么moveTile就是向右的,那么这个时候只有moveTile在它的目标位置的左边的时候才成立
		//也就是说它原来的位置nc是在目标的左边
		if(d == 0 && nc < nDestPos[moveTile][1])
			nh = h - 1;//nh比当前的h少1
		else if(d == 1 && nr < nDestPos[moveTile][0])//上
			nh = h - 1;
		else if(d == 2 && nc > nDestPos[moveTile][1])//右
			nh = h - 1;
		else if(d == 3 && nr > nDestPos[moveTile][0])
			nh = h - 1;
		else
			nh = h + 1;
		
		//大于f
		if(depth + nh > maxDepth)
			continue;
		
		//step.insert(step.begin() + depth, ns);
		//step.erase(step.begin() + depth + 1);
		
		pStepDir[depth] = d;
		stepHeap[depth] = ns;
		
		if(IDAstar(ns, depth + 1, nh, d))
			return true;
	}
	return false;
}


void PicturePizzle::ShowStep(int depth)
{
	int i = 0;
	//cout<<depth<<endl;
	
	for(i = 0; i < depth; i++)
		switch(pStepDir[i])
	{
		case 0:
			cout<<'l';
			break;
		case 1:
			cout<<'u';
			break;
		case 2:
			cout<<'r';
			break;
		case 3:
			cout<<'d';
			break;
	}
	cout<<endl;
	/*
	for(i = 0; i < depth; i++)
	{
	for(int r = 0; r < ROW; r++)
	{
	for(int c = 0; c < COL; c++)
	cout<<stepHeap[i].tile[r][c]<<"\t";
	cout<<endl;
	}
	cout<<endl<<endl;
	}
	*/
	
	/*
	cout<<step.size()<<endl<<endl;
	for(vector<Board>::iterator iter = step.begin(); iter != step.end(); iter++)
	{
	cout<<i++<<endl;
	for(int r = 0; r < 4; r++)
	{
	for(int c = 0; c < 4; c++)
				cout<<iter->tile[r][c]<<"\t";
				cout<<endl;
				}
				cout<<endl<<endl;
}*/
}

void PicturePizzle::Run()
{
	if(JudgeValidStatus())
	{
		maxDepth = Heuristic();
		while(maxDepth++)
			if(IDAstar(board, 0, h, 20))
				break;
	}
	else
	{
		cout<<"unsolvable"<<endl;
	}
	
}

void input(PicturePizzle &pp)
{
	//cout<<"输入一个文件(.txt),表示拼图的初始"
	//fstream file("in.txt");
	int num;
	for(int r = 0; r < ROW; r++)
		for(int c = 0; c < COL; c++)
		{
			//file>>num;
			cin>>num;
			if(/*file.fail()*/cin.fail())
			{
				//file.clear();
				//file.ignore(1,'\t');
				cin.clear();
				cin.ignore(1, ' ');
				pp.board.tile[r][c] = ROW * COL;
				pp.board.r = r;
				pp.board.c = c;
			}
			else
				pp.board.tile[r][c] = num;
		}
		//file.close();
}



int main()
{
	//freopen("out.txt", "w", stdout);
	
	PicturePizzle pp;
	input(pp);
	pp.Run();
	
	return 0;
}

posted @ 2011-01-16 21:29  Cranny  阅读(557)  评论(0编辑  收藏  举报