您的位置:首页 > 其它

POJ 2286 UVA 1343 The Rotation Game IDA*

2016-09-06 11:28 561 查看
以前看刘汝佳的方法写过,现在再写一遍

以下是自己意淫的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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: