LA 6621 /ZOJ 3736 Pocket Cube 打表+暴力

这道题是长沙区域赛的一道简单题,当时题目在ZOJ重现的时候就做了一次,但是做的好复杂,用的BFS暴力,而且还没打表,最后还是莫名其妙的爆栈错误,所以就一直没弄出来,昨天做到大白书上例题05年东京区域赛的一道类似题 Colored Cubes,这类题都需要脑补一下立方体的旋转总共有几种状态,然后用函数或者手工打表来记叙每一次变化之后的立方体变化

这道题从编号开始就给了我们很大便利,从0-23,读入数据也是按照这个来,很方便。我是手工打的表,说实话,魔方的转动还是有点复杂,我还真不知道用函数怎么打出表来。

吐血的是我一开始脑子居然觉得只有4种旋转方式,其实有6种,当然不考虑对应面的旋转,否则就有12种了,很好理解,魔方的一个面顺时针或者逆时针旋转,就相当于对应面的反向运动,因此6个面只需考虑三个面,由此打好表,然后用DFS进行下搜索即可。。。不得不说一下一旦用到DFS剪枝的重要性,由于魔方旋转过程中,可能出现转过去又转回来的情况,但其实是做了无用功,因此可以简单的判断一下是否又转了回来,这样子时间复杂度一下从1000+降到了300+。

#include <iostream>
#include <cstdio>
#include <cstring>
int clocks[6][24]={
{6,1,12,3,5,11,16,7,8,9,4,10,18,
13,14,15,20,17,22,19,0,21,2,23},
{20,1,22,3,10,4,0,7,8,9,11,5,2,
13,14,15,6,17,12,19,16,21,18,23},
{2,0,3,1,6,7,8,9,23,22,10,11,12,
13,14,15,16,17,18,19,20,21,5,4},
{1,3,0,2,23,22,4,5,6,7,10,11,12,
13,14,15,16,17,18,19,20,21,9,8},
{0,1,11,5,4,16,12,6,2,9,10,17,13,
7,3,15,14,8,18,19,20,21,22,23},
{0,1,8,14,4,3,7,13,17,9,10,2,6,12,
16,15,5,11,18,19,20,21,22,23},
};
int cuba[30],n,ans;
int r[10];
inline int max(int a,int b)
{
    if (a>b) return a;
    else  return b;
}
int judge(int* t)
{
    int flag,tot;
    tot=6;
    flag=t[0];
    if (flag!=t[1]||flag!=t[2]||flag!=t[3])
        tot--;
    flag=t[4];
    if (flag!=t[5]||flag!=t[10]||flag!=t[11])
        tot--;
    flag=t[6];
    if (flag!=t[7]||flag!=t[12]||flag!=t[13])
        tot--;
    flag=t[8];
    if (flag!=t[9]||flag!=t[14]||flag!=t[15])
        tot--;
    flag=t[16];
    if (flag!=t[17]||flag!=t[18]||flag!=t[19])
        tot--;
    flag=t[20];
    if (flag!=t[21]||flag!=t[22]||flag!=t[23])
        tot--;
    return tot;
}
void solve(int x)
{
    int temp[30],i,j;
    memcpy(temp,cuba,sizeof (cuba));
    for (i=0;i<x;i++)
    {
        int tt[30];
        memcpy(tt,temp,sizeof (temp));
        for (j=0;j<24;j++)
        {
            temp[j]=tt[clocks[r[i]][j]];
        }
    }
    ans=max(ans,judge(temp));
}
void dfs(int x)
{
    if (ans==6) return;
    solve(x);
    if (x==n) return;
    else
    {
        for (int i=0;i<6;i++)
        {
            if (x>2 && i==r[x-1] && i==r[x-2] && i==r[x-3]) //这个和下面那句都是剪枝条件,尤其是下面那句剪枝效果明显,避免重复的搜索。
                continue;
            if (x>0 && (i^1)==r[x-1])
                continue;
            r[x]=i;
            dfs(x+1);
        }
    }
}
int main()
{
    while (scanf("%d",&n)!=EOF)
    {
        for (int i=0;i<24;i++)
            scanf("%d",&cuba[i]);
        ans=0;
        memset(r,0,sizeof r);
        dfs(0);
        printf("%d
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/kkrisen/p/3536240.html