HDU 1430(康拓展开hash)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kzn2683331518/article/details/81277156

链接:http://acm.hdu.edu.cn/showproblem.php?pid=1430

题面:

在魔方风靡全球之后不久,Rubik先生发明了它的简化版——魔板。魔板由8个同样大小的方块组成,每个方块颜色均不相同,可用数字1-8分别表示。任一时刻魔板的状态可用方块的颜色序列表示:从魔板的左上角开始,按顺时针方向依次写下各方块的颜色代号,所得到的数字序列即可表示此时魔板的状态。例如,序列(1,2,3,4,5,6,7,8)表示魔板状态为:

1 2 3 4
8 7 6 5

对于魔板,可施加三种不同的操作,具体操作方法如下:

A: 上下两行互换,如上图可变换为状态87654321
B: 每行同时循环右移一格,如上图可变换为41236785
C: 中间4个方块顺时针旋转一格,如上图可变换为17245368

给你魔板的初始状态与目标状态,请给出由初态到目态变换数最少的变换步骤,若有多种变换方案则取字典序最小的那种。

题目分析:

看到最小状态转移步骤自然的想到bfs,所以需要对魔板的状态进行hash处理,这里状态数量有8!种,康托展开能够将每种排序都转换成一个唯一的序列号,而且全部状态的总序列号恰好在n!内分布。

以下贴网上大佬们的康托展开介绍:

康æå±å¼1

康æå±å¼2

所以运用康托展开+bfs就能搞定从12345678序列转移到任意序列所需的状态数最少。

但题目给的是一个初始状态和一个目标状态,所以我们需要建立hash对应数组m[],将初始状态对应成12345678序列,进行求解即可》》

代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;

string ans[45000];
int F[10], m[10];


void cantor(int s[], ll num, int k){//康托展开,把一个数字num展开成一个数组s,k是数组长度
	bool h[10] = { 0 };     //0到k-1,表示是否出现过
	for (int i = 0; i < k; i++){
		int t = num / F[k - i - 1];
		num %= F[k - i - 1];
		for (int j = 0, pos = 0;; j++, pos++){
			if (h[pos]) j--;
			if (j == t){
				h[pos] = true;
				s[i] = pos + 1;
				break;
			}
		}
	}
}

void inv_cantor(int s[], ll &num, int k){//康托逆展开,把一个数组s换算成一个数字num
	int cnt; num = 0;
	for (int i = 0; i < k; i++){
		cnt = 0;
		for (int j = i + 1; j < k; j++)
			if (s[i] > s[j]) cnt++;//判断几个数小于它
		num += F[k - i - 1] * cnt;
	}
}


inline void gao(int s[],char &ch){//这个搞事情函数容易写错。。我好菜啊,调了半天
    switch(ch){
    case 'A':for(int i=0;i<=3;i++)swap(s[i],s[7-i]);
        break;
    case 'B':for(int i=3;i>0;i--){swap(s[i],s[i-1]);swap(s[8-i],s[7-i]);}
        break;
    case 'C':swap(s[6],s[1]);swap(s[5],s[6]);swap(s[2],s[5]);
        break;
    }
}
void init(){
	F[0] = 1;       //预处理阶乘
	for (int i = 1; i <= 8; i++)F[i] = F[i - 1] * i;
	
	int s[8],t[8]; ll num,numt;  
	queue<ll>que; que.push(0ll);
	
	while(que.size()){
        num = que.front();que.pop();
        cantor(t,num,8); //把id转换成排列数组t
        for(char i='A';i<='C';i++){
            memcpy(s,t,sizeof(s));
            gao(s,i);   //搞事情函数,对应ABC三种操作,由于题目要求字典序最小,所以bfs拓展的顺序也就是ABC
            inv_cantor(s,numt,8);
            if(ans[numt].size()==0)
                ans[numt]=ans[num]+i,que.push(numt);
        }
	}
}
int main(){
    init();
    char n[10],t[10];ll num;int k[10];
    while(cin>>n>>t){
        if(strcmp(n,t)==0){//坑啊!如果没这个会输出AA,正确答案是输出空行
            cout<<endl;
            continue;
        }
        for(int i=0;i<8;i++)m[n[i]-'0']=i+1;
        for(int i=0;i<8;i++)k[i]=m[t[i]-'0'];
        inv_cantor(k,num,8);
        cout<<ans[num]<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/kzn2683331518/article/details/81277156
今日推荐