UVA1973*3用七个碎片拼成正方形,正方体的旋转和平移,即矩阵变换

52 篇文章 0 订阅
48 篇文章 0 订阅

状态搜索题,重点不是状态,而是这道题的代码,以及空间想象能力,当然,做个几个星期,以我的水平也不见得做的出,

看了一下别人的代码和题解,基本上理解了,这里先讲一下,正方体的变换:

1、首选我们要根据碎片,求出所有碎片的不重复的三维构形,可以推论得到碎片的最多不同构形有6*4=24种,其中6表示,无论是碎片还是立体都会有6个面,依次使6个面朝向自己,这样相当于有6种不同的旋转方式了(对于非对称立体是这样,对称的是不一定的),然后,在每个面朝向自己的构形状态下,都统一绕z/x/y(绕其中一个轴就行,轴的选定随意,但6个面朝向的构形都要统一绕此轴)轴绕0度,90度,180度,270度,这样就有4种不同构形(非对称),这样综合起来。对于对称 还是 非对称的立体构形最多有6*4=24种不同构形。这里非对称的a立体构形 刚好有24种旋转方式使其有24种不同构形,而对称的b~g的不同构形数量 是要小于24的。

2、经过这样的旋转变换后将得到的24种旋转构形,然后每一种构形还可以平移,平移理论上最多有3*3*3=27种,在每一种旋转上又产生相应的27种平移操作,得到24*27种不同构形状态,经过排序,去重(b~g产生的24中 是包含有重复构形的)等规格化处理,封装到一定的数据结构组织的库中,而这个库就是包含了a~g所有的旋转 平移 产生的不同的构形状态(a作特殊处理),这样就方便于后面的DFS 调用取舍这些构形状态。

这里RA数组用来储存不同的三维构形,然后这里figure数组用来储存除第一种碎片的其他六中碎片的不同形态,至于used的作用好像是用来标记碎片的状态是否碎片占据了正方体的

第一个位置,以此判断是否在最左下角的初始状态。注意他这里利用题中给的字符串得出不同碎片的方法比较巧妙,然后利用题中的字符串,把碎片a的状态先用tr_canoniza函数

转移到初始状态,然后对于每个a的状态,去递归搜索,在正方体中添碎片,直至能构成一个3*3正方体,然后存储答案。然后每次输出一个字符串后,先把这个字符串的a碎片平移旋转到初始状态,并且记录旋转的方法,然后再进行这个旋转把它旋转至输入状态,但不进行平移,然后根据24种旋转方法,把它旋转至初始位置,然后对于上面得出的所有答案进行这个旋转变换即是一个合理的答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<set>
#include<cmath>
#include<climits>
#include<cfloat>
#include<cctype>
#include<string>
#include<queue>
#include<cassert>
#include<cstring>
#define LL long long
using namespace std;
struct Partial
{
    char a[28];
    int used;
    bool operator <(const Partial &c) const {return memcmp(a,c.a,27)<0;}
    bool operator ==(const Partial &c) const {return memcmp(a,c.a,27)==0;}
    void print() {a[27]=0;printf("%s\n",a);}
    void fix_used()
    {
        used=0;
        for(int i=0;i<27;i++)
            if(a[i]!=0) used|=1<<i;
    }
};
struct Figure
{
    Partial *c;
    int n;
}figure[7];

vector<Partial> solutions;

void doit(Partial *cur,int fig)
{
    if(fig==7) {solutions.push_back(*cur);return;}

    Partial *p=figure[fig].c,next;

    for(int i=figure[fig].n;i-->0;p++)
    {
        if((cur->used &p ->used)==0)
        {
            for(int j=0;j<27;j++)
                next.a[j]=cur->a[j] | p->a[j];
            next.used = cur->used | p->used;
            doit(&next,fig+1);
        }
    }
}
 char RX[]={7,8,9,16,17,18,25,26,27,4,5,6,13,14,15,22,23,24,1,2,3,10,11,12,19,20,21};
 char RY[]={7,4,1,8,5,2,9,6,3,16,13,10,17,14,11,18,15,12,25,22,19,26,23,20,27,24,21};
 char RZ[]={3,12,21,6,15,24,9,18,27,2,11,20,5,14,23,8,17,26,1,10,19,4,13,22,7,16,25};
 char RA[64][32];

 void rot(char *s ,char *tab)
 {
     char t[32];
     for(int i=0;i<27;i++) t[tab[i]-1]=s[i];
     for(int i=0;i<27;i++) s[i]=t[i];
 }

 void make_rots()
 {
     char c[28];
     for(int i=0;i<27;i++) c[i]=i+1;

     int k=0;
     for(int x=0;x<4;x++,rot(c,RX))
        for(int y=0;y<4;y++,rot(c,RY))
        for(int z=0;z<4;z++,rot(c,RZ))
     {
         memcpy(RA[k++],c,27);
         for(int i=0;i<k-1;i++)
         if(memcmp(RA[i],RA[k-1],27)==0) {k--;break;}
     }
     assert(k == 24);
 }
 bool trans(char s[],int dx,int dy,int dz)
 {
     char t[28];
     for(int i=0;i<27;i++) t[i]=0;

     for(int x=0;x<3;x++)
        for(int y=0;y<3;y++)
        for(int z=0;z<3;z++)
     {
         int c=s[9*y+3*z+x];
         if(c!=0)
         {
             if(x+dx<0||x+dx>2) return 0;
             if(y+dy<0||y+dy>2) return 0;
             if(z+dz<0||z+dz>2) return 0;
             t[9*(y+dy)+3*(z+dz)+(x+dx)]=c;
         }
     }
     for(int i=0;i<27;i++)
        s[i]=t[i];
     return 1;
 }
 void tr_canoniza(char s[])
 {
     while(trans(s,-1,0,0));
     while(trans(s,0,-1,0));
     while(trans(s,0,0,-1));
 }
 int main()
 {
     make_rots();

     for(int fig='b';fig<='g';fig++)
     {
         vector<Partial> res;
         set<Partial> s;
         for(int r=0;r<24;r++)
         for(int dx=-3;dx<=3;dx++)
         for(int dy=-3;dy<=3;dy++)
         for(int dz=-3;dz<=3;dz++)
         {
             Partial c;

             char *pieces="adcaccaacddgbfgffedggbfebee";
             for(int i=0;i<27;i++)
                c.a[i]=(pieces[i]==fig)?fig:0;
             rot(c.a,RA[r]);
             if(!trans(c.a,dx,dy,dz)) continue;

             if(s.count(c)==0)
             {
                 c.fix_used();
                 res.push_back(c);
                 s.insert(c);
             }
         }
         Figure &F=figure[fig-'a'];
         F.n=res.size();
         F.c=new Partial[F.n];
         for(int i=0;i<F.n;i++)
            F.c[i]=res[i];
     }
     Partial Z;
     for(int i=0;i<27;i++)
        Z.a[i]=("aa.a..a...................."[i]=='a')?'a':0;
     tr_canoniza(Z.a);

     for(int dx=-3;dx<=3;dx++)
        for(int dy=-3;dy<=3;dy++)
        for(int dz=-3;dz<=3;dz++)
     {
         Partial Y=Z;
         if(!trans(Y.a,dx,dy,dz)) continue;
         Y.fix_used();
         doit(&Y,1);
     }
     for(;;)
     {
         Partial Q;
         for(int i=0;i<27;i++)
         {
             int c;
             while((c=getchar())!=EOF&&c!='.'&&c!='a');
             if(c==EOF) return 0;
             Q.a[i]=(c=='a')?'a':0;
         }
         int r;
         for(r=0;r<24;r++)
         {
             Partial C=Q;
             rot(C.a,RA[r]);
             tr_canoniza(C.a);
             if(C==Z) break;
         }
         if(r>=25) {cout<<endl;continue;}

         int rev;
         for(rev=0;rev<24;rev++)
         {
             Partial c=Q;
             rot(c.a,RA[r]);
             rot(c.a,RA[rev]);
             if(c==Q) break;
         }
         for(int i=0;i<(int)solutions.size();i++)
         {
             Partial C=solutions[i];
             rot(C.a,RA[rev]);
             C.print();
         }
         cout<<endl;
     }
     return 0;
 }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值