USACO 3.2.5 Magic Squares 魔板 msquare
This post is written in Chinese. Please consider using Google Translate
这道题类似于八数码难题,基本思想是宽搜,使用Hash判重。如果使用一般的八维数组空间可以达到8^8=16777216,会超过USACO的16MB空间限制。所以我们应该对状态进行散列存储,观察发现每位的数字不能重复,存在空间冗余。我们可以对于每个状态建立一个映射,采用康托展开算法。(参见Nocow) 定义cantor(s)为s串大大小顺序。可样将哈希容量缩减到8!=40320。
另外发现,对于魔板当前状态可以由一个8位的8进制数来表示,即无符号32位长整型 unsigned long 表示。 对于魔板的变换可以转化为对一个数字的位运算。这样可以大大提高程序效率。
程序有些长,但是效率很高。
USER: CmYkRgB CmYkRgB [cmykrgb1]
TASK: msquare
LANG: C++
Compiling...
Compile: OK
Executing...
Test 1: TEST OK [0.011 secs, 3476 KB]
Test 2: TEST OK [0.054 secs, 3444 KB]
Test 3: TEST OK [0.011 secs, 3476 KB]
Test 4: TEST OK [0.011 secs, 3472 KB]
Test 5: TEST OK [0.011 secs, 3480 KB]
Test 6: TEST OK [0.022 secs, 3476 KB]
Test 7: TEST OK [0.022 secs, 3476 KB]
Test 8: TEST OK [0.023 secs, 3484 KB]
All tests OK.
/*
ID: cmykrgb1
PROG: msquare
LANG: C++
*/
#include <iostream>
#include <fstream>
#define MAX 40320
using namespace std;
class state
{
public:
state()
{
comefrom=0;cur=0;cont=0;
}
state *comefrom;
unsigned long cur;
short cont;
};
state O;
state S[MAX];
class queue
{
private:
long first,last;
long list[MAX];
public:
long size;
queue()
{
reset();
}
void reset()
{
first=0;
size=0;
last=-1;
}
void ins(state *comefrom,unsigned long cur,long cont)
{
size++;
last++;
list[last]=last;
S[last].comefrom=comefrom;
S[last].cur=cur;
S[last].cont=cont;
}
long del()
{
size--;
return list[first++];
}
};
ifstream fi("msquare.in");
ofstream fo("msquare.out");
bool hash[MAX];
char B[4]={0,'A','B','C'};
long fac[8]={1,1,2,6,24,120,720,5040};
unsigned long Finish;
unsigned long cantor(unsigned long S)
{
long x=0,i,p,k,j;
bool hash[8]={false};
for (i=8;i>=2;i--)
{
k=S>> 3*(i-1);
S-=k<<3*(i-1);
hash[k]=true;
p=k;
for (j=0;j<=k-1;j++)
if (hash[j])
p--;
x+=fac[i-1]*p;
}
return x;
}
void init()
{
long i,t;
for (i=0;i<8;i++)
{
fi>>t;
t--;
O.cur*=8;
O.cur+=t;
}
Finish=cantor(O.cur);
hash[0]=true;
}
unsigned long turn(unsigned long S,int f)
{
long i;
unsigned long P=0;
switch (f)
{
case 1:
for (i=1;i<=8;i++)
P+=(S & (7<< ( (i-1)*3) ) )>> (i-1)*3 << ((8-i)*3);
return P;
break;
case 2:
P+=(S & 07000)>>3*3;
P+=(S & 00777)<<3;
P+=(S & 070000)<<3*3;
P+=(S & 077700000)>>3;
return P;
break;
case 3:
P= (S & 070077007);
P+=(S & 070)<<5*3;
P+=(S & 0700)>>1*3;
P+=(S & 0700000)>>3*3;
P+=(S & 07000000)>>1*3;
return P;
break;
}
}
void print(long ed,long f)
{
string O;
state *P=&S[ed];
char OUT[MAX];
long cnt=0,i;
while (P->cont)
{
cnt++;
OUT[cnt]=B[P->cont];
P=P->comefrom;
}
fo << cnt+1<<endl;
for (i=cnt;i>=1;i--)
fo << OUT[i];
fo << B[f] << endl;
fi.close();
fo.close();
exit(0);
}
void bfs()
{
long i=0,j;
queue Q;
Q.ins(&O,001234567,0);
while (Q.size)
{
unsigned long p,ct;
i=Q.del();
for (j=1;j<=3;j++)
{
p=turn(S[i].cur,j);
ct=cantor(p);
if (!hash[ct])
{
hash[ct]=true;
if (ct==Finish)
print(i,j);
Q.ins(&S[i],p,j);
}
}
}
}
int main()
{
init();
bfs();
fo << '0' <<'n'<<'n';
fi.close();
fo.close();
return 0;
}