2 12X453786 12345678X 564178X23 7568X4123Sample Output
Case 1: 2 dd Case 2: 8 urrulldr
题意:8数码的进阶版,这次是从A状态到达B状态确保一定有解。
嗯,表示A*算法过不了这题的,估计是数据比较多的那种。
注意:上一次的8数码的问题,只是要找到一个解就可以,所以A*算法的优化有两个权值函数h和g。并不影响最后的结果。因为只需要最快找到解就可以。
但是这一次不一样,你需要找到一个步数最少的解,你可以理解为g最小,然后还是要字典序最小的,你只需要调整一下搜索顺序就可以了。
附上一下A*这里启发式函数用的曼哈顿距离。
#include
#include
#include
#include
#include
#include
using namespace std;
const int inf = 1e8;
const int MAXN = 4e5;
int n,m;
int temp[10];
int x[10],y[10];
int aim;
char step[4] = {'d','l','r','u'};
int dx[4] = {1,0,0,-1};
int dy[4] = {0,-1,1,0};
int fac[9]= {1,1,2,6,24,120,720,5040,40320}; //康拖展开判重
int vis[MAXN];
int pre[MAXN];
//得到状态
int get_state(int *s)
{
int sum=0;
for(int i=0; i<9; i++)
{
int num=0;
for(int j=i+1; j<9; j++)
if(s[j]a.f;
}
};
void bfs(node x)
{
memset(vis,-1,sizeof(vis));
memset(pre,-1,sizeof(pre));
priority_queueq;
q.push(x);
vis[x.state] = 1;
while(!q.empty())
{
node u = q.top();
q.pop();
if(u.state == aim)
{
string ans;
while(u.state != -1)
{
ans += step[vis[u.state]];
u.state = pre[u.state];
}
printf("%d\n",ans.size()-1);
for(int i = ans.size()-2 ; i >= 0 ; --i)printf("%c",ans[i]);
puts("");
return ;
}
//4种交换
int x = u.pos/3;
int y = u.pos%3;
for(int i = 0 ; i < 4; ++i)
{
int nx = x + dx[i];
int ny = y + dy[i];
if(nx >= 0 && nx < 3 && ny >= 0 && ny < 3)
{
node v = u;
swap(v.num[u.pos],v.num[nx*3+ny]);
v.state = get_state(v.num);
if(vis[v.state] == -1)
{
v.pos = nx*3+ny;
v.g++;
v.h = get_h(v.num);
v.f = v.g + v.h;
vis[v.state] = i;
pre[v.state] = u.state;
q.push(v);
}
}
}
}
}
int main()
{
int t,ca = 0;
char s[15];
scanf("%d",&t);
while(t--)
{
node u;
scanf("%s",s);
for(int i = 0 ; i < 9; ++i)
{
if(s[i] == 'X')
{
u.num[i] = 0;
u.pos = i;
}
else u.num[i] = s[i] - '0' ;
}
u.state = get_state(u.num);
u.g = 0;
u.h = get_h(u.num);
u.f = u.g + u.h;
scanf("%s",s);
for(int i = 0 ; i < 9 ; ++i)
{
if(s[i] == 'X')temp[i] = 0;
else temp[i] = s[i] - '0' ;
}
aim = get_state(temp);
for(int i = 0 ;i < 9 ; ++i)
{
x[temp[i]] = i/3;
y[temp[i]] = i%3;
}
ca++;
printf("Case %d: ",ca);
bfs(u);
}
return 0;
}
然而是TLE的,可能是组数比较多的那种。上面一个题也不难想到一种预处理的办法,但是内存限制了一半。
然后这个题采用预处理的办法用了33196KB。所以上一题预处理会超内存。
这个题A*不可过,预处理则可以。不过估计双向bfs(划掉)也可破。字典序啊喂,双向bfs根本无法控制的好吧。什么可破啊,我试了一晚上最终发现这根本不可控QAQ。因为前半字典序最小+后半字典序最小不等于前半加后半字典序最小!!!!!!!譬如说aaaaab和aaabba的例子
你会输出aaabba,实际上最好的则是aaaaab。
预处理:这里你需要知道其实每个串无非就9种情况,只与X的位置有关,其余的你可以直接做一个映射关系。
char eight_num[9][10] = {"X12345678" , "1X2345678" , "12X345678" , "123X45678" ,
"1234X5678" , "12345X678" , "123456X78" , "1234567X8" , "12345678X" };
比如说示例1中的
12X453786我们就可以做这样的一个映射“12X345678”
分别是:1 → 1,2 → 2,X → X,4 → 3,5 → 4,3 → 5,7 → 6,8 → 7,6 → 8,
这样我们只需要搜索8种基本状态记录路径就可以了。
然后目标状态一样要映射掉。
这样就可以得到答案了。
#include
#include
#include
#include
#include
#include
using namespace std;
const int inf = 1e8;
const int MAXN = 4e5;
int n,m;
char eight_num[9][10] = {"X12345678" , "1X2345678" , "12X345678" , "123X45678" ,
"1234X5678" , "12345X678" , "123456X78" , "1234567X8" , "12345678X" };
char step[4] = {'d','l','r','u'};
int dx[4] = {1,0,0,-1};
int dy[4] = {0,-1,1,0};
int fac[9]= {1,1,2,6,24,120,720,5040,40320}; //康拖展开判重
int vis[9][MAXN];
int pre[9][MAXN];
//得到状态
int get_state(int *s)
{
int sum=0;
for(int i=0; i<9; i++)
{
int num=0;
for(int j=i+1; j<9; j++)
if(s[j]= 0 ; --i)printf("%c",ans[i]);
puts("");
}
void bfs(node x,int kind)
{
queueq;
q.push(x);
vis[kind][x.state] = 1;
while(!q.empty())
{
node u = q.front();
q.pop();
//4种交换
int x = u.pos/3;
int y = u.pos%3;
for(int i = 0 ; i < 4; ++i)
{
int nx = x + dx[i];
int ny = y + dy[i];
if(nx >= 0 && nx < 3 && ny >= 0 && ny < 3)
{
node v = u;
swap(v.num[u.pos],v.num[nx*3+ny]);
v.state = get_state(v.num);
if(vis[kind][v.state] == -1)
{
v.pos = nx*3+ny ;
vis[kind][v.state] = i;
pre[kind][v.state] = u.state;
q.push(v);
}
}
}
}
}
void init(char *s,int kind)//预处理
{
node u;
for(int i = 0 ; i < 9; ++i)
{
if(s[i] == 'X')
{
u.num[i] = 0;
u.pos = i;
}
else u.num[i] = s[i] - '0' ;
}
u.state = get_state(u.num);
bfs(u,kind);
}
int main()
{
int t,ca = 0;
char s[15],c[15];
int temp[MAXN];
int kind;
memset(vis,-1,sizeof(vis));
memset(pre,-1,sizeof(pre));
for(int i = 0 ;i < 9 ; ++i)init(eight_num[i],i);
scanf("%d",&t);
while(t--)
{
scanf("%s",s);
int cnt = 1;
//设置映射初始状态8数码
for(int i = 0 ;i < 9 ; ++i)
{
if(s[i] == 'X')
{
c[0] = 0;
kind = i;
}
else c[s[i] - '0'] = cnt++;
}
//映射目标8数码
scanf("%s",s);
for(int i = 0 ; i < 9 ; ++i)
{
if(s[i] == 'X')temp[i] = c[0];
else temp[i] = c[s[i] - '0'] ;
}
int aim = get_state(temp);//得到目标状态
ca++;
printf("Case %d: ",ca);
print(aim,kind);
}
return 0;
}