POJ 2286 UVA 1343 The Rotation Game IDA*

以前看刘汝佳的方法写过,现在再写一遍

以下是自己意淫的IDA*

g()是旋转的次数 ;

h()的求法:每次移动最多只会使得中央区域包含的数字种类减少1种。求出中央区域个数最多的那个数字的个数 n, 要达到中央区域数字都相同,至少需要 8-n次操作,此即估价函数值

//         1     2
//         3     4
//   5  6  7  8  9  10 11
//         12    13
//   14 15 16 17 18 19 20  
//         21    22
//         23    24
#include<cstdio>
#include<cstring>
#include<queue> 
#include<set> 
#include<string> 
#include<algorithm>
#include<iostream>
#define min3(x,y,z) min(x,min(y,z))
using namespace std;
const int center[]={7,8,9,12,13,16,17,18};
const int Move[8][7]={
	{1,3,7,12,16,21,23},
	{2,4,9,13,18,22,24},
	{11,10,9,8,7,6,5},
	{20,19,18,17,16,15,14},
	{24,22,18,13,9,4,2},
	{23,21,16,12,7,3,1},
	{14,15,16,17,18,19,20},
	{5,6,7,8,9,10,11}
};
const char d[]="ABCDEFGH";
int Board[25];
int diff(int *B,int n){
	int cnt=0;
	for(int i=0;i<8;i++)
		if(B[center[i]]!=n) cnt++;
	return cnt;
}
int H(int *B){
	return min3(diff(B,1),diff(B,2),diff(B,3));
}
struct Node{
	int Board[25]; 
	string path; 
	int f,g,h;
	Node(int *b,int g,string p):g(g),path(p){memcpy(Board,b,sizeof(Board));h=H(b);f=g+h;}
	bool operator < (const Node& n) const{
		if(f==n.f) return path>n.path;  //保证字典序 
		return f>n.f;
	}
};
set<long long> closed;
bool inClosed(int* B){
	long long code=0;
	for(int i=1;i<=24;i++){
		code|=B[i];
		code<<=2;
	}
	if(closed.count(code)) return true;
	closed.insert(code);
	return false;
}
bool A_Star(int maxd)
{
	priority_queue<Node> open; closed.clear();
	open.push(Node(Board,0,""));
	int nBoard[25];
	while(!open.empty())
	{
		Node u=open.top(); open.pop();
		if(u.h==0){
			cout<<(u.g?u.path:"No moves needed")
			<<endl<<u.Board[7]<<endl;
			return true;
		}
		if(u.g+u.h>maxd) continue;
		for(int i=0;i<8;i++)
		{
			memcpy(nBoard,u.Board,sizeof(nBoard));
			for(int j=0;j<6;j++)   
				nBoard[Move[i][j]]=u.Board[Move[i][j+1]];
			nBoard[Move[i][6]]=u.Board[Move[i][0]];
			
			if(inClosed(nBoard)) continue;
			open.push(Node(nBoard,u.g+1,u.path+d[i]));
		}
	}
	return false; 
}
int main()
{
	while(scanf("%d",&Board[1])&&Board[1])
	{
		for(int i=2;i<=24;i++) scanf("%d",&Board[i]);
		for(int i=1;i<=24;i++) if(!Board[i]) break;
		for(int maxd=1;;maxd++)
			if(A_Star(maxd)) break;
	}
	return 0;
}


结果WR 因为答案求出来并非字典序最小,我的策略是优先比较f(),如果f()相同就比较字典序,而有可能f()大的反而字典序小,因为h()中并未体现字典序,所以单单比较f并非字典序最小,

输入 1 2 2 3 3 3 1 1 1 1 2 2 2 2 3 3 3 3 3 1 1 1 1 1 正确的是:GBBCGAAA ,而我的是GBBGCAAA

所以只能比较g,且在g相同的时候path最小,类似BFS

比较函数改成如下就AC了

struct Node{
	int Board[25]; 
	string path; 
	int f,g,h;
	Node(int *b,int g,string p):g(g),path(p){memcpy(Board,b,sizeof(Board));h=H(b);f=g+h;}
	bool operator < (const Node& n) const{
		if(g==n.g) return path>n.path;
		return g>n.g;
	}
};


但超级慢,2000MS+

所以不如用DFS,首先是天然的字典序,其次无需浪费判重的时间,而且深搜索一开始不会像BFS重复


BFS如下:160MS

//         1     2
//         3     4
//   5  6  7  8  9  10 11
//         12    13
//   14 15 16 17 18 19 20  
//         21    22
//         23    24
#include<cstdio>
#include<cstring>
#include<queue> 
#include<set> 
#include<string> 
#include<algorithm>
#include<iostream>
#define min3(x,y,z) min(x,min(y,z))
using namespace std;
const int center[]={7,8,9,12,13,16,17,18};
const int rev[]={5,4,7,6,1,0,3,2};
const int Move[8][7]={
	{1,3,7,12,16,21,23},
	{2,4,9,13,18,22,24},
	{11,10,9,8,7,6,5},
	{20,19,18,17,16,15,14},
	{24,22,18,13,9,4,2},
	{23,21,16,12,7,3,1},
	{14,15,16,17,18,19,20},
	{5,6,7,8,9,10,11}
};
int Board[25];
int diff(int n){
	int cnt=0;
	for(int i=0;i<8;i++)
		if(Board[center[i]]!=n) cnt++;
	return cnt;
}
int H(){
	return min3(diff(1),diff(2),diff(3));
}
int solved(){
	for(int i=1;i<8;i++)
		if(Board[center[0]]!=Board[center[i]]) return false;
	return true;
}
void move(int i){
	int tmp=Board[Move[i][0]];
	for(int j=0;j<6;j++)   
		Board[Move[i][j]]=Board[Move[i][j+1]];
	Board[Move[i][6]]=tmp;
}
char ans[1000];
bool DFS(int dep,int maxd)
{
	if(solved()) {
		ans[dep]='\0';
		return true;
	}
	if(dep+H()>maxd) return false;
	for(int i=0;i<8;i++){
		ans[dep]='A'+i;
		move(i);
		if(DFS(dep+1,maxd)) return true;
		move(rev[i]);
	}
	return false;
}
int main()
{
	while(scanf("%d",&Board[1])&&Board[1])
	{
		for(int i=2;i<=24;i++) scanf("%d",&Board[i]);
		if(solved()) cout<<"No moves needed"<<endl;
		else{
			for(int maxd=1;;maxd++)
				if(DFS(0,maxd)) break;
			cout<<ans<<endl;
		}
		cout<<Board[7]<<endl;
	}
	return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值