【浅谈康托展开】HDU1043[Eight]题解
来源:互联网 发布:布尔教育java怎么样 编辑:程序博客网 时间:2024/06/05 03:39
题目概述
给出一个3*3的矩阵,其中有一个格子是空格,其他都是数字,每次移动可以将格子附近的数字移到格子中,同时原先的数字变成格子。给定一个初始状态和最终状态,求一个方案。
解题报告
显然状态顶多只有9!=362880个,所以我们就会想到Bfs。但是如何判断一个状态是否出现过呢?用set肯定是可以的,但效率不是太高(常数也大),用hash也是可以的,然而完美的hash函数是存不下来的(当成9进制数)。这里就有一个比较好用的东西叫康托展开,其实也是hash,但是hash函数不但是完美的,而且空间开销也达到理论下限:9!=362880,非常适合储存全排列。
其实康托展开并不难理解,以274563018(下面称为当前排列)为例:
第一位为2,那么第一位为0,1的所有排列都比当前排列小,总计2*8!个。
第一位为2,第二位为7,那么第二位为0,1,3,4,5,6(没有2,第一位用过了)的所有排列都比当前排列小,总计6*7!个。
第一位为2,第二位为7,第三位为4,那么第三位为0,1,3的所有排列都比当前排列小,总计3*6!个。
……
所以,假设排列有n位,a[i]表示i+1~n中比i位上的数小的数的个数,那么比当前排列小的排列总个数就等于:
康托展开还有逆运算,就是已知有多少个排列比当前排列小,求当前排列。以208745为例,过程非常类似转进制:
208745/(8!)=5,说明有5个数比第一位小,即0,1,2,3,4,所以第一位为5。然后208745%(8!)=7145。
7145/(7!)=1,说明有1个数比第二位小,即0,所以第二位为1。然后7145%(7!)=2105。
2105/(6!)=2,说明有2个数比第三位小,即0,2(没有1,第二位用过了),所以第二位为3,然后2105%(6!)=665。
……
有了康托展开和逆运算,这道题就迎刃而解了……才怪!我写完了Bfs之后狂TLE不止,怎么改都超时。然后当我A了POJ之后我发现HDU多组数组我才疯狂TLE,于是想到了预处理。所以我从最终状态开始遍历,预处理了所有起始状态,然后从TLE变成了156ms……
示例程序
#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=362880;const int fac[9]={1,1,2,6,24,120,720,5040,40320};const int fl[4]={-1,1,-3,3};const int lst[9]={1,2,3,4,5,6,7,8,0};const char op[4]={'l','r','u','d'};struct data {int x,fa;char ch;};int gl,who[maxn+5],fst[9];data que[maxn+5];bool vis[maxn+5];void Travel(int now){ if (now==1) return; putchar(que[now].ch);Travel(que[now].fa);}bool check(int x,char ch){ if (x<0||x>8) return false; if (ch=='l'&&x%3==2) return false; if (ch=='r'&&x%3==0) return false; return true;}int Contor(const int *a) //康托展开{ int pos=0; for (int i=0;i<=8;i++) { int tot=0; for (int j=i+1;j<=8;j++) tot+=a[i]>a[j]; //有tot个数比a[i]小 pos+=tot*fac[9-i-1]; } return pos+1;}void INV_Contor(int pos,int *a) //康托展开逆运算{ bool vis[9];pos--;memset(vis,0,sizeof(vis)); for (int i=0;i<=8;i++) { int now=pos/fac[9-i-1],j; for (j=0;j<=8;j++) if (!vis[j]) if (now==0) break; else now--; //第now个出现的就是第i位 a[i]=j;vis[j]=true; //j出现过了,标记 pos%=fac[9-i-1]; }}int getp(int *a) {for (int i=0;i<=8;i++) if (!a[i]) return i;}bool Bfs(){ memset(vis,0,sizeof(vis)); int Head=0,Tail=0;que[++Tail]=(data){Contor(lst),0,0}; while (Head!=Tail) { int x[9],now[9],p; INV_Contor(que[++Head].x,x);p=getp(x); //逆运算求当前状态x for (int i=0;i<=3;i++) if (check(p+fl[i],op[i])) { memcpy(now,x,sizeof(now));swap(now[p],now[p+fl[i]]); int pos=Contor(now); //用康托展开将now变为数字 if (!vis[pos]) { que[++Tail]=(data){pos,Head,op[i^1]}; //反向处理,所以是op[i^1] vis[pos]=true;who[pos]=Tail; } } } return false;}char getrch(){ char ch=getchar(); while (('9'<ch||ch<'0')&&ch!='x') { if (ch==EOF) return EOF; ch=getchar(); } if (ch=='x') return '0'; return ch;}int main(){ freopen("program.in","r",stdin); freopen("program.out","w",stdout); Bfs();char ch; while ((ch=getrch())!=EOF) { fst[0]=ch-48;for (int i=1;i<=8;i++) fst[i]=getrch()-48; int pos=Contor(fst); if (!who[pos]) printf("unsolvable"); else Travel(who[pos]); putchar('\n'); } return 0;}
- 【浅谈康托展开】HDU1043[Eight]题解
- POJ1077 HDU1043 Eight 八数码 (A*+康托展开)
- HDU1043:Eight(A*+康托)
- HDU1043:Eight(A*+康托)
- HDU1043:Eight(A*+康托)
- HDU1043:Eigth(康托展开)
- hdu1043 Eight(A*/双向BFS/单项BFS打表+康托展开)
- POJ1077 HDU1043 Eight 八数码第四境界 双向广搜 康托展开 逆康托
- POJ1077&HDU1043 Eight 八数码第八境界 IDA* hash 康托展开 奇偶剪枝
- POJ1077&HDU1043 Eight 八数码第七境界 AStar hash 康托展开 最小堆优化 奇偶剪枝
- hdu3567 Eight II(IDA*+康托展开)
- hdu 1043 eight (搜索 + 康托展开)
- 【POJ1077】Eight-A*+康托展开
- [BFS+康托展开]Hdu 1043 Eight
- HDU 1043 Eight(康托展开)
- [POJ]1077 Eight 八数码:康托展开+BFS
- 【康托展开+状压BFS】poj1077 Eight(八数码问题)
- HDU 1043 Eight(bfs+康托展开)
- 2016多重背包1019
- EOJ-----重复数
- centos7下安装网易云音乐教程及相关依赖关系解决
- C++中printf无法输出问题
- 介绍vue-cli
- 【浅谈康托展开】HDU1043[Eight]题解
- c语言如何从txt文件读取数据
- could not initialize proxy
- N25Q00AA NOR SPIFLASH 的FPGA驱动开发
- Spring连接数据库
- 业界资讯:白鹭引擎5.0发布,支持webassembly
- 小白的2017书单(一)生活
- 信号处理第六课:一个向量用一组基底来表达
- Yaf通过composer整合Smarty