魔方(cube.cpp)
题目描述:
给出一个二阶魔方,保证 N 步以内能够还原。“还原”被定义为每个面均为纯色。
请给出,操作编号字典序最小,且不存在同类操作相邻,的还原方案。
输入格式:
第一行一个正整数N,表示最多步数。
接下来24个整数,按上图的顺序依次给出Ci,Ci∈{1,2,3,4,5,6}。
输出格式:
一行,t个用空格隔开的正整数,表示复原的最小字典序操作序列,要求0 < t ⩽ N。
最后一个数后无空格,数据保证输入魔方是打乱的。
注:(1,2,3) 虽然长度长于(2,3),但字典序更小。
样例读入:
2
1 1 1 1
4 4 2 2
6 6 3 3
3 3 6 6
5 5 5 5
4 4 2 2
样例输出:
2
样例解释:
因为不能类别相同的操作相邻,所以只有2种操作方式可以在两步内复原此时的魔方:(2),(17),故字典序最小的为(2)。
数据范围:
对于 20% 的数据,保证 N = 1
对于 40% 的数据,保证 N ⩽ 3
对于另20%的数据,保证 N ⩽ 6,且保证答案只用到前6种操作
对于100% 的数据,保证 N ⩽ 7
题解:一开始看到数据范围还以为是状态压缩,将魔方的每个状态表示出来,结果发现不对。其实就是大模拟,如果每次枚举18个操作,时间复杂度是O(n^18),显然会超时,只能拿60分。其实不难发现,不管怎样,答案中的操作只能在1~9之间,比如底面顺时针90度=顶面顺时针270度,底面180度=顶面180度,底面270度=顶面90度。所以只用枚举一面即可,于是就AC了。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
int n;
int tag[20];
int work[30],flag=0,opt[30];
int fa=0;
unsigned long long get_hash()
{
fa++;
unsigned long long Hash=0;
for(int i=1;i<=8;i++) Hash=Hash*11+work[i];
return Hash;
}
void init()
{
for(int i=1;i<=3;i++) tag[i]=tag[18-i+1]=1;
for(int i=4;i<=6;i++) tag[i]=2;
for(int i=7;i<=9;i++) tag[i]=3;
for(int i=10;i<=12;i++) tag[i]=2;
for(int i=13;i<=15;i++) tag[i]=3;
}
void change_(int id)
{
int a,b,c;
switch(id)
{
case 1:{//ding
a=work[5];b=work[6];c=work[1];
work[5]=work[13];work[6]=work[14];
work[13]=work[24];work[14]=work[23];
work[23]=work[10];work[24]=work[9];
work[9]=a;work[10]=b;
work[1]=work[3];work[3]=work[4];work[4]=work[2];work[2]=c;
break;
}
case 2:{
swap(work[5],work[24]);swap(work[6],work[23]);
swap(work[13],work[9]);swap(work[14],work[10]);
swap(work[1],work[4]);swap(work[2],work[3]);
break;
}
case 3:{
a=work[5];b=work[6];c=work[1];
work[5]=work[9];work[6]=work[10];
work[9]=work[24];work[10]=work[23];
work[23]=work[14];work[24]=work[13];
work[13]=a;work[14]=b;
work[1]=work[2];work[2]=work[4];work[4]=work[3];work[3]=c;
break;
}
case 4:{
a=work[1];b=work[3];c=work[9];
work[1]=work[21];work[3]=work[23];
work[21]=work[17];work[23]=work[19];
work[17]=work[5];work[19]=work[7];
work[5]=a;work[7]=b;
work[9]=work[11];work[11]=work[12];work[12]=work[10];work[10]=c;
break;
}
case 5:{
swap(work[1],work[17]);swap(work[3],work[19]);
swap(work[5],work[21]);swap(work[7],work[23]);
swap(work[9],work[12]);swap(work[10],work[11]);
break;
}
case 6:{
a=work[1];b=work[3];c=work[9];
work[1]=work[5];work[3]=work[7];
work[5]=work[17];work[7]=work[19];
work[17]=work[21];work[19]=work[23];
work[21]=a;work[23]=b;
work[9]=work[10];work[10]=work[12];work[12]=work[11];work[11]=c;
break;
}
case 7:{
a=work[3];b=work[4];c=work[5];
work[3]=work[12];work[4]=work[10];
work[10]=work[17];work[12]=work[18];
work[17]=work[15];work[18]=work[13];
work[13]=a;work[15]=b;
work[5]=work[7];work[7]=work[8];work[8]=work[6];work[6]=c;
break;
}
case 8:{
swap(work[3],work[18]);swap(work[4],work[17]);
swap(work[13],work[12]);swap(work[15],work[10]);
swap(work[5],work[8]);swap(work[6],work[7]);
break;
}
case 9:{
a=work[3];b=work[4];c=work[5];
work[3]=work[13];work[4]=work[15];
work[13]=work[18];work[15]=work[17];
work[17]=work[10];work[18]=work[12];
work[10]=b;work[12]=a;
work[5]=work[6];work[6]=work[8];work[8]=work[7];work[7]=c;
break;
}
}
}
void dfs(int dep,int last)
{
bool cct=0;
for(int i=0;i<6;i++)
{
int tmp=work[(i*4)+1];
for(int j=1;j<=4;j++)
if(work[(i*4)+j]!=tmp)
{
cct=1;break;
}
if(cct) break;
}
if(!cct)
{
flag=1;dep--;
for(int i=1;i<dep;i++) printf("%d ",opt[i]);printf("%d\n",opt[dep]);
return ;
}
if(dep==n+1) return ;
int ccr[50];
for(int i=0;i<6;i++)
for(int j=1;j<=4;j++)
ccr[(i*4)+j]=work[(i*4)+j];
for(int i=0;i<=2;i++)
{
if(last==i) continue;
for(int j=(i*3+1);j<=(i+1)*3;j++)
{
if(flag) return ;
change_(j);opt[dep]=j;
dfs(dep+1,i);
if(flag) return ;
for(int k=0;k<6;k++)
for(int fe=1;fe<=4;fe++)
work[(k*4)+fe]=ccr[(k*4)+fe];
}
}
}
int main()
{
// freopen("cube.in","r",stdin);
// freopen("cube.out","w",stdout);
init();
int x;
scanf("%d",&n);
for(int i=0;i<6;i++)
for(int j=1;j<=4;j++)
scanf("%d",&x),work[(i*4)+j]=x;
dfs(1,-1);
return 0;
}