一个正方体,每个面标了一个数字,问,这个正方体,在8*8的棋盘上,从起点滚动到终点,所有朝下的面的数字之和的最小值(包括起点和终点)。
正方体,经过旋转,最多有24种状态。建立分层图,点数:24*8*8=1536. 稀疏图,用 SPFA 算法求最短路。
我直接写的一维数组。节点编号= 正方体的状态*64 +横坐标*8 +纵坐标 ;
开始求出所有24个状态(特殊情况可能少于24种状态)之间的状态转移。
然后SPFA求最短路。
using namespace std; int dx[4]={-1, 1, 0, 0}; int dy[4]={ 0, 0, 1,-1}; int de[4]={ 5, 3, 1, 0}; struct Dice{ int a[6];//按照题目给的顺序,分别为前,后,上,右,下,左 int next[4];//四个方向旋转后的编号 void turn(int i){ switch(i){ case 0:left();break; case 1:right();break; case 2:up();break; case 3:down();break; } } void left(){int t=a[5];a[5]=a[2];a[2]=a[3];a[3]=a[4];a[4]=t;}//向左滚动 void right(){int t=a[5];a[5]=a[4];a[4]=a[3];a[3]=a[2];a[2]=t;}//向右滚动 void up(){int t=a[0];a[0]=a[4];a[4]=a[1];a[1]=a[2];a[2]=t;}//向上滚动 void down(){int t=a[0];a[0]=a[2];a[2]=a[1];a[1]=a[4];a[4]=t;}//向下滚动 bool operator ==(const Dice&B)const{ int T=0; for(int i=0;i<6;i++) T+=a[i]==B.a[i]; return T==6; } void show(){ printf("\t(%d,%d,%d,%d,%d,%d)\n",a[0],a[1],a[2],a[3],a[4],a[5]); } }D[24];int Dn;//记录24种状态 struct Node{ int path;//记录最短路径 int Dis;//记录最小值 void init(int k){ Dis=-1;path=k; } }game[1536]; queue<int> Q; bool inq[1536]; void Bfs(int k){ int tempk=k; int index=tempk/64;tempk%=64; int x=tempk/8,y=tempk%8; for(int i=0;i<4;i++){//向4个方向扩展 int nx=x+dx[i],ny=y+dy[i]; int Next=D[index].next[i]*64+nx*8+ny;//计算下个节点的编号 if(nx>=0&&nx<8&&ny>=0&&ny<8){//如果下一点在棋盘内 int Distance=game[k].Dis+D[index].a[de[i]]; if(!~game[Next].Dis||Distance<game[Next].Dis){//如果未走过或者路径更短,更新 game[Next].Dis=Distance; game[Next].path=game[k].path; game[Next].path=k; if(!inq[Next]) Q.push(Next),inq[Next]=1;//若未入队,入队 } } } } void F(int i){//输出路径 if(i!=game[i].path) F(game[i].path); int t=i%64; printf(" %c%c",t/8+'a',t%8+'1'); } int main(void) { //处理输入 string a;int Sx,Sy,Dx,Dy; cin>>a;Sx=a[0]-'a';Sy=a[1]-'1'; cin>>a;Dx=a[0]-'a';Dy=a[1]-'1'; for(int i=0;i<6;i++) scanf("%d",&D[0].a[i]); //求出24状态之间的状态转移。 Dn=1; for(int i=0;i<Dn;i++){//对于每个状态(开始只有一个状态) for(int k=0;k<4;k++){// 向四个方向滚动 Dice temp=D[i]; temp.turn(k);//记录滚动后的状态 int Target=-1; for(int j=0;j<Dn;j++){//若出现过,记录编号 if(D[j]==temp) { Target=j;break; } } if(!~Target){//若没出现过,用新的编号 Target=Dn++; D[Target]=temp; } D[i].next[k]=Target;//记录i往四个方向转动后的状态的编号 } } //显示Dices /* for(int i=0;i<Dn;i++){ printf("%d:",i);D[i].show(); for(int k=0;k<4;k++) printf("->%d",D[i].next[k]); cout<<endl; }*/ //计算 (SPFA) for(int i=0;i<1536;i++) game[i].init(i); memset(inq,0,sizeof(inq)); Q.push(Sx*8+Sy);inq[Sx*8+Sy]=1; game[Sx*8+Sy].Dis=D[0].a[4]; while(!Q.empty()){ int cnt=Q.front();Q.pop(); inq[cnt]=0; Bfs(cnt); } //搜索答案 int ANS=-1;int ANSI=-1; for(int i=0;i<Dn;i++){//搜索每一层 if(~game[i*64+Dx*8+Dy].Dis){//如果可以到达 if(!~ANS||game[i*64+Dx*8+Dy].Dis<ANS){//ANS未找到,或者新距离小于原始ANS 则更新。 ANS=game[i*64+Dx*8+Dy].Dis; ANSI=i*64+Dx*8+Dy; } } } //输出答案 cout<<ANS; F(ANSI); cout<<endl; return 0; }