zoukankan      html  css  js  c++  java
  • POJ 3567 Eight II (八数码问题+bfs+康托展开)

    题目链接

    http://acm.hdu.edu.cn/showproblem.php?pid=3567

    解题思路

    这道题是POJ 1077 Eight的升级版, 区别在于POJ1077的终点是确定的,那么其他情况都是可以由这一种情况推出. 这道题的起点和终点似乎都是不确定的,如果暴力搜索的话一定会超时,这就很难办. 可以从另一个角度来思考, 我们根据起点中'X'的位置来进行分类,而其他位置的棋子用其编号(1~8)来表示即可,这样把起点从(9!)中情况缩小到了9种情况,以这9种情况事先进行一个搜索就可以了.
    输出路径要求是字典最小序,以d, l, r, u的顺序搜索即可

    搜索思路

    这道题的另一个难点----也是八数码问题的核心----在于如何标识每一种情况, 方法是利用康托展开算法进行哈希

    康托展开是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩。 康托展开的实质是计算当前排列在所有由小到大全排列中的顺序,因此是可逆的。
    康托展开的转换公式: (X=a_n(n-1)!+a_{n-1}(n-2)!+cdots+a_1cdot0!),其中(a_i)为整数,并且(0leq a_i<i,1leq ileq n)
    对公式的解释: 设(m_i)是数组中从后往前数第i个数, 则(a_i)(m_i)后面的数中比(m_i)小的数的个数
    举例:3 5 7 4 1 2 9 6 8 展开为 98884。因为X=2*8!+3*7!+4*6!+2*5!+0*4!+0*3!+2*2!+0*1!+0*0!=98884.
    解释:
    排列的第一位是3,比3小的数有两个,以这样的数开始的排列有8!个,因此第一项为2*8!
    排列的第二位是5,比5小的数有1、2、3、4,由于3已经出现,因此共有3个比5小的数,这样的排列有7!个,因此第二项为3*7!
    以此类推,直至0*0!
    ----维基百科

    AC代码

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <string>
    #include <queue>
    #include <stack>
    
    using namespace std;
    
    const int maxn = 4e5;
    
    int a[10];
    int cnt[10];
    int tmp[9];
    stack<char> ans;
    char sp[15], ep[15];
    int pos, tar[9];
    int mp[9][9] = {//9代表'X'
        9, 1, 2, 3, 4, 5, 6, 7, 8,
        1, 9, 2, 3, 4, 5, 6, 7, 8,
        1, 2, 9, 3, 4, 5, 6, 7, 8,
        1, 2, 3, 9, 4, 5, 6, 7, 8,
        1, 2, 3, 4, 9, 5, 6, 7, 8,
        1, 2, 3, 4, 5, 9, 6, 7, 8,
        1, 2, 3, 4, 5, 6, 9, 7, 8,
        1, 2, 3, 4, 5, 6, 7, 9, 8,
        1, 2, 3, 4, 5, 6, 7, 8, 9
    };
    
    struct node{
    	int num[9];
    	int pos;
    	int pre;
    	char path;
    	node(): pos(-1), path(0){}
    }que[9][maxn];
    
    int cant(int *ori){    //康托展开
    	for (int i = 1; i < 10; ++i){
    		cnt[i] = i - 1;
    	}
    	int s = 0;
    	for (int i = 0; i < 9; ++i){
    		s += cnt[ori[i]] * a[8 - i];
    		for (int j = ori[i]; j < 10; ++j){
    			--cnt[j];
    		}
    	}
    	return s;
    }
    
    void bfs(const int p, int rt){
    	queue<int> q;
    	q.push(rt);
    	int ct, frt;
    	node * cur;
    	while (!q.empty()){
    		frt = q.front();
    		q.pop();
    		cur = &que[p][frt];
    		memcpy(tmp, cur->num, sizeof tmp);
    		//0: down
    		if (cur->pos < 6){
    			swap(tmp[cur->pos], tmp[cur->pos + 3]);
    			ct = cant(tmp);
    			if (que[p][ct].pos == -1){
    				que[p][ct].pos = cur->pos + 3;
    				memcpy(que[p][ct].num, tmp, sizeof tmp);
    				que[p][ct].pre = frt;
    				que[p][ct].path = 'd';
    				q.push(ct);
    			}
    			swap(tmp[cur->pos], tmp[cur->pos + 3]);
    		}
    		//1: left
    		if (cur->pos % 3 != 0){
    			swap(tmp[cur->pos], tmp[cur->pos - 1]);
    			ct = cant(tmp);
    			if (que[p][ct].pos == -1){
    				que[p][ct].pos = cur->pos - 1;
    				memcpy(que[p][ct].num, tmp, sizeof tmp);
    				que[p][ct].pre = frt;
    				que[p][ct].path = 'l';
    				q.push(ct);
    			}
    			swap(tmp[cur->pos], tmp[cur->pos - 1]);
    		}
    		//2: right
    		if (cur->pos % 3 != 2){
    			swap(tmp[cur->pos], tmp[cur->pos + 1]);
    			ct = cant(tmp);
    			if (que[p][ct].pos == -1){
    				que[p][ct].pos = cur->pos + 1;
    				memcpy(que[p][ct].num, tmp, sizeof tmp);
    				que[p][ct].pre = frt;
    				que[p][ct].path = 'r';
    				q.push(ct);
    			}
    			swap(tmp[cur->pos], tmp[cur->pos + 1]);
    		}
    		//3: up
    		if (cur->pos >= 3){
    			swap(tmp[cur->pos], tmp[cur->pos - 3]);
    			ct = cant(tmp);
    			if (que[p][ct].pos == -1){
    				que[p][ct].pos = cur->pos - 3;
    				memcpy(que[p][ct].num, tmp, sizeof tmp);
    				que[p][ct].pre = frt;
    				que[p][ct].path = 'u';
    				q.push(ct);
    			}
    			swap(tmp[cur->pos], tmp[cur->pos - 3]);
    		}
    	}
    }
    
    int main(){
    	a[0] = 1;
    	for (int i = 1; i < 10; ++i){
    		a[i] = i * a[i - 1];
    	}
    	for (int i = 0; i < 9; ++i){
    		int ct = cant(mp[i]);
    		que[i][ct].pos = i;
    		memcpy(que[i][ct].num, mp[i], sizeof que[i][ct].num);
    		bfs(i, ct);
    	}
    	int cs;
    	scanf("%d", &cs);
    	for (int t = 1; t <= cs; ++t){
    		scanf("%s", sp);
    		scanf("%s", ep);
    		int p;//'X'的位置
    		for (p = 0; ; ++p){
    			if (sp[p] == 'X'){
    				break;
    			}
    		}
    		int tp;
    		for (tp = 0; ; ++tp){
    			if (ep[tp] == 'X'){
    				break;
    			}
    		}
    
                    //用棋子的编号来标识终点
    		for (int i = 0; i < 9; ++i){
    			for (int j = 0; j < 9; ++j){
    				if (ep[i] == sp[j]){
    					tar[i] = mp[p][j];
    				}
    			}
    		}
    		int ct = cant(tar);
    
    		while (!ans.empty()) ans.pop();
    		while (que[p][ct].path){
    			ans.push(que[p][ct].path);
    			ct = que[p][ct].pre;
    		}
    		printf("Case %d: %d
    ", t, ans.size());
    		while (!ans.empty()){
    			printf("%c", ans.top());
    			ans.pop();
    		}
    		printf("
    ");
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    TimelineJS JSON 数据格式
    FFMS2 API 译文 [原创]
    用 Delphi 7 实现基于 FFMS2 的视频转 GIF 工具 [原创]
    FFMS2 官方说明译文 [原创]
    华为悦盒 EC6108V9U 破解过程全记录(root扫盲) [原创]
    Delphi 中的 RectTracker
    Delphi 7中对StretchBlt, StretchDIBits, DrawDibDraw, BitBlt 的性能测试
    【最后一篇API译文】Android开发-API指南- Contacts Provider
    Android开发-API指南- Calendar Provider
    Android开发-API指南-数据存储
  • 原文地址:https://www.cnblogs.com/lucianosimon/p/7436528.html
Copyright © 2011-2022 走看看