Wiki OI 1225 八数码难题

题目链接:http://wikioi.com/problem/1225/

算法与思路:康托展开(hash)+ bfs;

这个题目的的搜索部分对于很多同学来说并不难,关键在于棋盘状态的储存;

如果直接使用0~8组成的9位数作为数组下标,要开到8亿显然不现实,

这个时候就要用到康托展开了;

初次接触的同学请戳链接 康托展开-维基百科,上面讲的很详细。

然后就是相对简单的bfs了,详见注释。

#include<stdio.h>
char st[9] = {0};  
struct node
{
	int val;   //康托值 
	int step;  //步数 
}map[400000];
int vis[400000];
int factor[] = {1,1,2,6,24,120,720,5040,40320,362880};//阶乘 
void swap(char *a, char *b)
{
     int temp = *a;
     *a = *b;
     *b = temp;
}
int cantor(char x[]) //康托展开 
{
 	int sum = 0, s;
 	for(int i = 0; i < 9; i++)
 	{
  		s = 0;
  		for(int j = i + 1; j < 9; j++)
   			if(x[j] < x[i])
		   		s++;
  		sum += s * factor[9 - i - 1];
 	}
 	return sum;
}
void trans(int x) //康托展开逆运算 
{
 	int visit[10] = {0};
 	int t;
 	for(int i = 0; i < 9; i++)
 	{
  		t = x / factor[9 - i - 1];
  		x %= factor[9 - i - 1];
  		for(int j = 0; j <= t; j++)
   			if(visit[j] == 1)
    			t++;
  		visit[t] = 1;
  		st[i] = t;
 	}
}
int main()
{
 	
 	int zero;	//0的位置 
 	int aim = 46685;	//目标状态康托值 
 	int head = 0, tail = 1;		//首尾指针 
 	for(int i = 0; i < 9; i++)
 	{
  		scanf("%c", &st[i]);
  		st[i] -= '0';
 	}
 	map[0].val = cantor(st); 	//纪录初始状态的康托值 
 	vis[map[0].val] = 1;		//标记此状态,以便判重 
    while(head < tail) 
    {
        if(aim == map[head].val)  //找到目标状态就结束 
			break;
        trans(map[head].val);
        for(int i = 0; i < 9; i++)  //寻找当前状态0的位置 
            if(st[i] == 0)
            {
            	zero = i;
            	break;
            }
        if(zero + 3 <= 8)	//向下 
        {
             swap(&st[zero], &st[zero+3]);  //交换0与下方数字位置 
             map[tail].val = cantor(st);    //求出交换后的康托值 
             if(vis[map[tail].val] == 0)
             {
                 vis[map[tail].val] = 1;
                 map[tail].step = map[head].step + 1; //记录步数 
                 tail++;
             }
             swap(&st[zero], &st[zero+3]);
        }
        if(zero - 3 >= 0)	//向上 
        {
             swap(&st[zero], &st[zero-3]);
             map[tail].val = cantor(st);
             if(vis[map[tail].val] == 0)
             {
                 vis[map[tail].val] = 1;
                 map[tail].step = map[head].step + 1;
                 tail++; 
             }
             swap(&st[zero], &st[zero-3]);    
        }
        if(zero + 1 >= 0 && zero % 3 != 2)//判断向右是否合法 
        {
             swap(&st[zero], &st[zero+1]);
             map[tail].val = cantor(st);
             if(vis[map[tail].val] == 0)
             {
                 vis[map[tail].val] = 1;
                 map[tail].step = map[head].step+1;   
                 tail++;
             }
             swap(&st[zero], &st[zero+1]);                 
        }
        if(zero - 1 >= 0 && zero % 3 != 0)//判断向左是否合法  
        {
             swap(&st[zero], &st[zero-1]);
             map[tail].val = cantor(st);
             if(vis[map[tail].val] == 0)
             {
                 vis[map[tail].val] = 1;
                 map[tail].step = map[head].step + 1;   
                 tail++;
             }
             swap(&st[zero], &st[zero-1]);    
        }
        head++;
    }
    printf("%d\n", map[head].step);
 	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值