模拟赛【色子】题解--spfa

色子(cube)

【问题描述】
A 有一个爱好,就是玩色子。现在在一个国际象棋的棋盘上摆放着一个色子
(立方体),色子的棱长等于棋盘上每个格子的边长。色子的六个面上都标上了
数字 num。色子可以向其前、后、左、右四个方向滚动,在滚动的过程中,色子
底部与棋盘接触的那个面上的数字被加入总和。现在要找到一条从开始位置到结
束位置的路径,使得色子在此路径上滚动所得的总和最小,色子在开始位置和结
束位置上的地面数字也要被计入总和,起始位置和结束位置是不同的。
【输入格式】
输入仅一行。首先给出开始位置和结束位置,(在棋盘上的位置表示,由字
符’a’-‘h’表示列,由数字’1’-‘8’表示行)。然后是立方体在开始格子上各个面上的 6
个数字,顺序是:前、后、上、右、下、左面。
【输出格式】
输出最小的总和.
【样例输入】
e2 e3 0 8 1 2 1 1
【样例输出】
5
【样例解释】
e2 d2 d1 e1 e2 e3
【数据范围】
100%的数据,棋盘边长=8,num<=1000.
30%的数据,步数<=8;

【题目解释】
根据题目模拟。

【题目类型】
模拟(最短路)

【解题报告】
其实此题就是色子的六个面的状态比较难考虑,数组也比较大,但是经过仔细的考虑后,我们可以发现其实至少只需考虑两个相邻的面的变化(因为知道两个相邻的面就可以知道与这两个面都相邻的面和它们的对面即可以推出六个面的变化),但两个相邻的面写起来比较繁琐,所以本人就考虑了比较好写的三个相邻的面的变化(有精力的童鞋可以去写两个相邻的面的)。

【示例代码】

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=10,maxm=13825,op[6]={1,0,4,5,2,3},flg[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int ans,p[maxn],dst[maxn][maxn][6][6][6];
bool vis[maxn][maxn][6][6][6];
struct wjd
{
    int x,y,a,b,c;
}que[maxm];
inline int read_()
{
    int sum=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') sum=sum*10+ch-48,ch=getchar();
    return sum;
}
struct jz
{
    int x,y;
    void sread_()
    {
        char ch=getchar();
        while (ch<'a'||ch>'z') ch=getchar();
        y=ch-96; x=read_();
    }
}s,t;
bool check_(int x,int y)
{
    if (x<1||y<1||x>8||y>8) return false;
    return true;
}
void change_(int x,int &a,int &b,int &c)//推出色子翻滚后的面的编号
{
    int aa=op[a],bb=op[b],cc=op[c],t;
    if (x==0) {t=a; a=b; b=aa; aa=bb; bb=t;}
    if (x==1) {t=bb; bb=c; c=b; b=cc; cc=t;}
    if (x==2) {t=a; a=bb; bb=aa; aa=b; b=t;}
    if (x==3) {t=bb; bb=cc; cc=b; b=c; c=t;}
}
void spfa_(int x,int y)//spfa刷最短路
{
    memset(dst,63,sizeof(dst));
    memset(vis,0,sizeof(vis));
    vis[x][y][0][4][5]=1; dst[x][y][0][4][5]=p[4]; que[1].x=x; que[1].y=y; que[1].a=0; que[1].b=4; que[1].c=5;
    int hed=0,til=1;
    while (hed!=til)
    {
        hed=(hed+1)%maxm;
        x=que[hed].x; y=que[hed].y; int a=que[hed].a,b=que[hed].b,c=que[hed].c;
        vis[x][y][a][b][c]=0;
        for (int i=0; i<=3; i++)
        {
            int xx=x+flg[i][0],yy=y+flg[i][1],aa=a,bb=b,cc=c;
            if (!check_(xx,yy)) continue;
            change_(i,aa,bb,cc);
            if (dst[x][y][a][b][c]+p[bb]>dst[xx][yy][aa][bb][cc]) continue;
            dst[xx][yy][aa][bb][cc]=dst[x][y][a][b][c]+p[bb];
            if (xx==t.x&&yy==t.y&&dst[xx][yy][aa][bb][cc]<ans) ans=dst[xx][yy][aa][bb][cc];
            if (vis[xx][yy][aa][bb][cc]) continue;
            vis[xx][yy][aa][bb][cc]=1;
            til=(til+1)%maxm;
            que[til]=(wjd){xx,yy,aa,bb,cc};
        }
    }
}
int main()
{
    freopen("cube.in","r",stdin);
    freopen("cube.out","w",stdout);
    s.sread_(); t.sread_();
    for (int i=0; i<6; i++) p[i]=read_();
    ans=1<<30;
    spfa_(s.x,s.y);
    printf("%d",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值