康托展开(八数码问题)
来源:互联网 发布:弯刀对着瓢切菜 知乎 编辑:程序博客网 时间:2024/05/17 08:49
定义:
把一个整数X展开成如下形式:
X=a[n]
其中a[i]为当前未出现的元素中是排在第几个(从0开始),并且0<=a[i]
应用
求一个数字串排列在字典序下的编号(即第几个)。当然也可以倒着用,即给你编号求数字串。
可以对一些算法进行优化,有点hash的味道。
实现:
①求一个数字串排列在字典序下的编号:
其实定义里头的那个公式求的就是排列a之前的排列数。代码模拟一下即可。
这里采用的是递归实现:
long long dfs(int x){ if (x>n) return 0; long long sum=0; int num=0; for (int i=1;i<a[x];i++) if (!f[i])//如果这个数并没有出现过 num++; f[a[x]]=true; sum+=num*jc[n-x];//jc[i]即i的阶乘 sum+=dfs(x+1);//递归 return sum;}
②倒着用:
反过来想,对于当前的编号n,我们要取的数。
不难发现,第n位的数即为num/jc[sum-n]。
仍然为递归实现:
void dfs1(long long x,int num){ if (num==n){//因为题目要求行末不能有空格 for (int i=1;i<=n;i++)//最后一个数字其实已经确定,即仍然没有出现的那个 if (!f[i]){ printf("%d",i); break; } return; } int dd=x/jc[n-num]; int now=dd; for (int i=1;i<=n;i++){ if (!f[i]) if (dd==0){ printf("%d ",i); f[i]=true; break; } else dd--; } dfs1(x-now*jc[n-num],num+1);//递归}
③算法优化:
这里举一个最经典的例子:八数码问题。
给出一个3*3的矩阵,其中有一个格子是空格,其他都是数字,每次移动可以将格子附近的数字移到格子中,同时原先的数字变成格子。给定一个初始状态和最终状态,求最少步数。
当然,这道题的解法很多,但这里只讲康托展开。
简单的BFS的话是会T掉的,因为判重的问题。而康托展开在这道题中的作用就是判重。把3*3的序列转化为其字典序,就能够存的下了(jc[9]=362880,即有不到40万的状态)。判重也就能实现了。
代码(写的太烂,跑得比较慢,二维压成一维就可以省去我的calc的过程):
#include<cstdio>#include<cstring>#include<algorithm>#define MAXN 3#define MAXM 400000using namespace std;const int t1[4]={0,1,0,-1};const int t2[4]={1,0,-1,0}; bool num[MAXM+5];int que[MAXM+5][2];int a[MAXN+5][MAXN+5],b[MAXN+5][MAXN+5],jc[MAXN*MAXN+5];int n=3;bool f[MAXM+5];int calc(int s[MAXN+5][MAXN+5]){ int sum=0; for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) sum=sum*10+s[i][j]; return sum;}void fz(int s[MAXN+5][MAXN+5],int zhi,int &x,int &y){ for (int i=n;i>=1;i--) for (int j=n;j>=1;j--){ s[i][j]=zhi%10; zhi/=10; if (s[i][j]==0){ x=i; y=j; } }}int jisuan(int x){//康托展开 memset(f,false,sizeof(f)); int s[MAXN*MAXN+5]; for (int i=MAXN*MAXN;i>=1;i--){ s[i]=x%10; x/=10; } int sum=0; for (int i=1;i<=MAXN*MAXN;i++){ int num=0; for (int j=0;j<s[i];j++) if (!f[j]) num++; f[s[i]]=true; sum+=num*jc[MAXN*MAXN-i]; } return sum;}int main(){ for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) scanf("%d",&a[i][j]);//初始状态 for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) scanf("%d",&b[i][j]);//目标状态 jc[1]=1; for (int i=2;i<=MAXN*MAXN;i++) jc[i]=jc[i-1]*i;//算阶乘 int r=0,w=1; que[1][1]=calc(a); que[1][0]=1; memset(num,0,sizeof(num)); while (r<w){//BFS int xx[MAXN+5][MAXN+5]; int x,y; memset(xx,0,sizeof(xx)); if (que[++r][1]==calc(b)){ printf("%d\n",que[r][0]); break; } fz(xx,que[r][1],x,y); for (int i=0;i<=n;i++){ int p=x+t1[i],q=y+t2[i]; if (p>=1&&p<=3&&q>=1&&q<=3){ swap(xx[x][y],xx[p][q]); int l=calc(xx); int k=jisuan(l); if (!num[k]){ que[++w][1]=calc(xx); que[w][0]=que[r][0]+1; num[k]=true; } swap(xx[x][y],xx[p][q]); } } } return 0;}
阅读全文
1 0
- 康托展开(八数码问题)
- 康托展开求八数码问题
- 八数码(康托展开)
- 【康托展开+状压BFS】poj1077 Eight(八数码问题)
- POJ 1077 Eight 八数码问题[康托展开 + BFS]
- hdu1043Eight (经典的八数码)(康托展开+BFS)
- hdu1043Eight (经典的八数码)(康托展开+BFS)
- BFS+康托展开(洛谷1379 八数码难题)
- POJ 1077 八数码(康托展开+暴力bfs)
- HDU1043BFS 康托展开 八数码
- hdu 1043 /poj 1077 Eight(经典八数码问题,BFS+康托展开)
- 八数码问题的初次解决(康托展开+bfs)
- 蓝桥杯 历届试题 九宫重排 (八数码问题--康托展开去重 + bfs搜索)
- poj 1077 bfs+康托展开(8数码问题)
- 借助八数码问题,双向广搜,康托展开,逆序数奇偶性
- 蓝桥杯 历届试题 九宫重排 经典八数码问题 A*算法+康托展开
- [POJ]1077 Eight 八数码:康托展开+BFS
- codevs1225 八数码难题(A*搜索+康托展开)
- 首发博客,记录心情
- qml如何进行动态翻译
- 2017年8月18日提高组T2 队伍统计
- 文本三剑客之sed
- 大量数据如何排序
- 康托展开(八数码问题)
- struts2 二
- 多视图学习
- 2017年9月1日-2017年9月3日训练总结
- bzoj 1620: [Usaco2008 Nov]Time Management 时间管理(贪心)
- As
- Token和session
- RecyclerView的使用
- 网络请求+ListView展示数据+数据库