状态总共只有8!个,不多,用广度优先找最优。
我的代码用了个很损的办法:HASH,用HASH表记录从初始达到每一种局面(8!==40320)的最优步数。说到底这也是一种动态规划(记忆化)的思路。
而在HASH表中,const BESTSTEP_HASH[]表示了每一种状态的值——用位运算算出来的,每个格占3位,例如0x53997,二进制表示为“001 010 011 100 101 110 111”,就是局面“1 2 3 4 5 6 7 8”。
查找的时候只需要算一下待查找局面的HASH值,二分查找(可以看一下,const是预先按顺序排好的)。
常数表开了384KB,自己都不忍看了。我在想是不是所有比赛都可以交这么长的程序。
USACO官方的解析用了最短路径,xD,有前途。值得一提的是,最短路径做时,图很稀疏,SPFA非常好用。
我的代码。提交2次——第一次用了一般的HASH结果爆内存。
常数表可以在这里下载:http://www.fs2you.com/files/a9022d51-2acb-11dd-964b-00142218fc6e/
/*
ID:
LANG: C++
TASK: msquare
*/
#include < cstdlib >
#include < iostream >
#include < fstream >
using namespace std;
const int BESTSTEP_HASH[ 40320 ] = {
0x53977, 0x5397e, 0x539af,
/*
常数表略去大部分,有300多KB。
如果要用,我建议自己生成。
当然你也可以从这里下载:http://www.fs2you.com/files/a9022d51-2acb-11dd-964b-00142218fc6e/
*/
, 0xfac642, 0xfac650, 0xfac681, 0xfac688
} ;
int beststep[ 40320 ];
struct QNODE
{
int step;
int state[8];
QNODE *next;
QNODE *pred;
char predproc;
} ;
int tar[ 8 ];
int nqueue;
QNODE * front, * rear;
inline int shash( int state[])
{
int sum, t;
sum = 0;
for (int i=0; i<8; i++)
{
t = state[i]-1;
t <<= i*3;
sum += t;
}
return sum;
}
int findkey( int key)
{
int l, r, mid;
l = 0; r = 40320-1;
while (l <= r)
{
mid = (l+r)/2;
if (key < BESTSTEP_HASH[mid]) r = mid-1;
else if (key > BESTSTEP_HASH[mid]) l = mid+1;
else break;
}
return mid;
}
char existbetter( int chkstate[], int chkstep)
{
int pos;
pos = findkey(shash(chkstate));
if (beststep[pos]==0 || beststep[pos]>chkstep) return 0;
else return 1;
}
void addnode( int newstate[], int newstep, char newproc)
{
QNODE *newnode;
newnode = new QNODE;
memcpy(newnode->state, newstate, 8*sizeof(int));
newnode->step = newstep;
newnode->next = NULL;
newnode->pred = front;
newnode->predproc = newproc;
rear->next = newnode;
rear = newnode;
nqueue++;
int pos;
pos = findkey(shash(newstate));
beststep[pos] = newstep;
}
int main()
{
ifstream fin("msquare.in");
for (int i=0; i<8; i++) fin >> tar[i];
fin.close();
QNODE *realfront;
front = rear = new QNODE;
realfront = front;
front->step = 0;
for (int i=0; i<8; i++) front->state[i] = i+1;
front->pred = NULL;
front->next = NULL;
nqueue = 1;
while (nqueue > 0)
{
char match;
match = 1;
for (int i=0; i<8; i++) if (front->state[i] != tar[i]) {match=0; break;}
if (match) break;
int nextstate[8];
//TRANSFORM 1
for (int i=0; i<8; i++) nextstate[i] = front->state[7-i];
if (!existbetter(nextstate, (front->step)+1))
addnode(nextstate, (front->step)+1, 'A');
//TRANSFORM 2
nextstate[0] = front->state[3];
for (int i=1; i<=3; i++) nextstate[i] = front->state[i-1];
nextstate[7] = front->state[4];
for (int i=4; i<=6; i++) nextstate[i] = front->state[i+1];
if (!existbetter(nextstate, (front->step)+1))
addnode(nextstate, (front->step)+1, 'B');
//TRANSFORM 3
nextstate[0] = front->state[0];
nextstate[3] = front->state[3];
nextstate[4] = front->state[4];
nextstate[7] = front->state[7];
nextstate[1] = front->state[6];
nextstate[2] = front->state[1];
nextstate[5] = front->state[2];
nextstate[6] = front->state[5];
if (!existbetter(nextstate, (front->step)+1))
addnode(nextstate, (front->step)+1, 'C');
//DELETE FRONT
//这是不能释放内存,因为需要用前驱推出状态
//如果只是求最优步数当然可以马上删
front = front->next;
nqueue--;
}
char *seq;
seq = new char[front->step + 1];
QNODE *curr;
int seqid;
seqid = front->step - 1;
curr = front;
while (curr->pred != NULL)
{
seq[seqid] = curr->predproc;
seqid--;
curr = curr->pred;
}
seq[front->step] = '';
ofstream fout("msquare.out");
fout << front->step << endl;
fout << seq << endl;
fout.close();
QNODE *delpos, *nextpos;
delpos = realfront;
while (delpos != NULL)
{
nextpos = delpos->next;
delete delpos;
delpos = nextpos;
}
return 0;
}
ID:
LANG: C++
TASK: msquare
*/
#include < cstdlib >
#include < iostream >
#include < fstream >
using namespace std;
const int BESTSTEP_HASH[ 40320 ] = {
0x53977, 0x5397e, 0x539af,
/*
常数表略去大部分,有300多KB。
如果要用,我建议自己生成。
当然你也可以从这里下载:http://www.fs2you.com/files/a9022d51-2acb-11dd-964b-00142218fc6e/
*/
, 0xfac642, 0xfac650, 0xfac681, 0xfac688
} ;
int beststep[ 40320 ];
struct QNODE
{
int step;
int state[8];
QNODE *next;
QNODE *pred;
char predproc;
} ;
int tar[ 8 ];
int nqueue;
QNODE * front, * rear;
inline int shash( int state[])
{
int sum, t;
sum = 0;
for (int i=0; i<8; i++)
{
t = state[i]-1;
t <<= i*3;
sum += t;
}
return sum;
}
int findkey( int key)
{
int l, r, mid;
l = 0; r = 40320-1;
while (l <= r)
{
mid = (l+r)/2;
if (key < BESTSTEP_HASH[mid]) r = mid-1;
else if (key > BESTSTEP_HASH[mid]) l = mid+1;
else break;
}
return mid;
}
char existbetter( int chkstate[], int chkstep)
{
int pos;
pos = findkey(shash(chkstate));
if (beststep[pos]==0 || beststep[pos]>chkstep) return 0;
else return 1;
}
void addnode( int newstate[], int newstep, char newproc)
{
QNODE *newnode;
newnode = new QNODE;
memcpy(newnode->state, newstate, 8*sizeof(int));
newnode->step = newstep;
newnode->next = NULL;
newnode->pred = front;
newnode->predproc = newproc;
rear->next = newnode;
rear = newnode;
nqueue++;
int pos;
pos = findkey(shash(newstate));
beststep[pos] = newstep;
}
int main()
{
ifstream fin("msquare.in");
for (int i=0; i<8; i++) fin >> tar[i];
fin.close();
QNODE *realfront;
front = rear = new QNODE;
realfront = front;
front->step = 0;
for (int i=0; i<8; i++) front->state[i] = i+1;
front->pred = NULL;
front->next = NULL;
nqueue = 1;
while (nqueue > 0)
{
char match;
match = 1;
for (int i=0; i<8; i++) if (front->state[i] != tar[i]) {match=0; break;}
if (match) break;
int nextstate[8];
//TRANSFORM 1
for (int i=0; i<8; i++) nextstate[i] = front->state[7-i];
if (!existbetter(nextstate, (front->step)+1))
addnode(nextstate, (front->step)+1, 'A');
//TRANSFORM 2
nextstate[0] = front->state[3];
for (int i=1; i<=3; i++) nextstate[i] = front->state[i-1];
nextstate[7] = front->state[4];
for (int i=4; i<=6; i++) nextstate[i] = front->state[i+1];
if (!existbetter(nextstate, (front->step)+1))
addnode(nextstate, (front->step)+1, 'B');
//TRANSFORM 3
nextstate[0] = front->state[0];
nextstate[3] = front->state[3];
nextstate[4] = front->state[4];
nextstate[7] = front->state[7];
nextstate[1] = front->state[6];
nextstate[2] = front->state[1];
nextstate[5] = front->state[2];
nextstate[6] = front->state[5];
if (!existbetter(nextstate, (front->step)+1))
addnode(nextstate, (front->step)+1, 'C');
//DELETE FRONT
//这是不能释放内存,因为需要用前驱推出状态
//如果只是求最优步数当然可以马上删
front = front->next;
nqueue--;
}
char *seq;
seq = new char[front->step + 1];
QNODE *curr;
int seqid;
seqid = front->step - 1;
curr = front;
while (curr->pred != NULL)
{
seq[seqid] = curr->predproc;
seqid--;
curr = curr->pred;
}
seq[front->step] = '';
ofstream fout("msquare.out");
fout << front->step << endl;
fout << seq << endl;
fout.close();
QNODE *delpos, *nextpos;
delpos = realfront;
while (delpos != NULL)
{
nextpos = delpos->next;
delete delpos;
delpos = nextpos;
}
return 0;
}
USACO官方
/*
Magic Squares
Hal Burch
This is a shortest path problem, where the nodes of the graph
are board arrangements, and edges are transformations. There
are 8! = 40,320 possible board arrangements, so the problem
can be solved using breadth-first search (since all edges are
of unit length.
Number the boards in increasing order lexicographically, so that
indexing is simpler.
In order to simplify the calculations, walk the path backward (start
at the ending arrangement given, find the minimum path to the
initial configuration following reverse transformations). Then, walk
backwards in the resulting tree to determine the path.
*/
#include < stdio.h >
#include < assert.h >
/* the distance from the initial configuration (+1) */
/* dist == 0 => no know path */
int dist[ 40320 ];
/* calculate the index of a board */
int encode( int * board)
{
static int mult[8] =
{1, 8, 8*7, 8*7*6, 8*7*6*5,
8*7*6*5*4, 8*7*6*5*4*3, 8*7*6*5*4*3*2};
/* used to calculate the position of a number within the
remaining ones */
int look[8] = {0, 1, 2, 3, 4, 5, 6, 7};
int rlook[8] = {0, 1, 2, 3, 4, 5, 6, 7};
/* rlook[look[p]] = p and look[rlook[p]] = p */
int lv, rv;
int t;
rv = 0;
for (lv = 0; lv < 8; lv++)
{
t = look[board[lv]]; /* the rank of the board position */
assert(t < 8-lv); /* sanity check */
rv += t * mult[lv];
assert(look[rlook[7-lv]] == 7-lv); /* sanity check */
/* delete t */
look[rlook[7-lv]] = t;
rlook[t] = rlook[7-lv];
}
return rv;
}
/* the set of transformations, in order */
static int tforms[ 3 ][ 8 ] = { {8, 7, 6, 5, 4, 3, 2, 1},
{4, 1, 2, 3, 6, 7, 8, 5}, {1, 7, 2, 4, 5, 3, 6, 8} } ;
void do_trans( int * inboard, int * outboard, int t)
{ /* calculate the board (into outboard) that results from doing
the t'th transformation to inboard */
int lv;
int *tform = tforms[t];
assert(t >= 0 && t < 3);
for (lv = 0; lv < 8; lv++)
outboard[lv] = inboard[tform[lv]-1];
}
void do_rtrans( int * inboard, int * outboard, int t)
{ /* calculate the board (into outboard) that which would result
in inboard if the t'th transformation was applied to it */
int lv;
int *tform = tforms[t];
assert(t >= 0 && t < 3);
for (lv = 0; lv < 8; lv++)
outboard[tform[lv]-1] = inboard[lv];
}
/* queue for breadth-first search */
int queue[ 40325 ][ 8 ];
int qhead, qtail;
/* calculate the distance from each board to the ending board */
void do_dist( int * board)
{
int lv;
int t1;
int d, t;
qhead = 0;
qtail = 1;
/* the ending board is 0 steps away from itself */
for (lv = 0; lv < 8; lv++) queue[0][lv] = board[lv];
dist[encode(queue[0])] = 1; /* 0 steps (+ 1 offset for dist array) */
while (qhead < qtail)
{
t1 = encode(queue[qhead]);
d = dist[t1];
/* for each transformation */
for (lv = 0; lv < 3; lv++)
{
/* apply the reverse transformation */
do_rtrans(queue[qhead], queue[qtail], lv);
t = encode(queue[qtail]);
if (dist[t] == 0)
{ /* found a new board position! add it to queue */
qtail++;
dist[t] = d+1;
}
}
qhead++;
}
}
/* find the path from the initial configuration to the ending board */
void walk(FILE * fout)
{
int newboard[8];
int cboard[8];
int lv, lv2;
int t, d;
for (lv = 0; lv < 8; lv++) cboard[lv] = lv;
d = dist[encode(cboard)];
/* start at the ending board */
while (d > 1)
{
for (lv = 0; lv < 3; lv++)
{
do_trans(cboard, newboard, lv);
t = encode(newboard);
if (dist[t] == d-1) /* we found the previous board! */
{
/* output transformatino */
fprintf (fout, "%c", lv+'A');
/* find the rest of the path */
for (lv2 = 0; lv2 < 8; lv2++) cboard[lv2] = newboard[lv2];
break;
}
}
assert(lv < 3);
d--;
}
fprintf (fout, " ");
}
int main( int argc, char ** argv)
{
FILE *fout, *fin;
int board[8];
int lv;
if ((fin = fopen("msquare.in", "r")) == NULL)
{
perror ("fopen fin");
exit(1);
}
if ((fout = fopen("msquare.out", "w")) == NULL)
{
perror ("fopen fout");
exit(1);
}
for (lv = 0; lv < 8; lv++)
{
fscanf (fin, "%d", &board[lv]);
board[lv]--; /* use 0-based instead of 1-based */
}
/* calculate the distance from each board to the ending board */
do_dist(board);
for (lv = 0; lv < 8; lv++) board[lv] = lv;
/* output the distance from and the path from the initial configuration */
fprintf (fout, "%d ", dist[encode(board)]-1);
walk(fout);
return 0;
}
Magic Squares
Hal Burch
This is a shortest path problem, where the nodes of the graph
are board arrangements, and edges are transformations. There
are 8! = 40,320 possible board arrangements, so the problem
can be solved using breadth-first search (since all edges are
of unit length.
Number the boards in increasing order lexicographically, so that
indexing is simpler.
In order to simplify the calculations, walk the path backward (start
at the ending arrangement given, find the minimum path to the
initial configuration following reverse transformations). Then, walk
backwards in the resulting tree to determine the path.
*/
#include < stdio.h >
#include < assert.h >
/* the distance from the initial configuration (+1) */
/* dist == 0 => no know path */
int dist[ 40320 ];
/* calculate the index of a board */
int encode( int * board)
{
static int mult[8] =
{1, 8, 8*7, 8*7*6, 8*7*6*5,
8*7*6*5*4, 8*7*6*5*4*3, 8*7*6*5*4*3*2};
/* used to calculate the position of a number within the
remaining ones */
int look[8] = {0, 1, 2, 3, 4, 5, 6, 7};
int rlook[8] = {0, 1, 2, 3, 4, 5, 6, 7};
/* rlook[look[p]] = p and look[rlook[p]] = p */
int lv, rv;
int t;
rv = 0;
for (lv = 0; lv < 8; lv++)
{
t = look[board[lv]]; /* the rank of the board position */
assert(t < 8-lv); /* sanity check */
rv += t * mult[lv];
assert(look[rlook[7-lv]] == 7-lv); /* sanity check */
/* delete t */
look[rlook[7-lv]] = t;
rlook[t] = rlook[7-lv];
}
return rv;
}
/* the set of transformations, in order */
static int tforms[ 3 ][ 8 ] = { {8, 7, 6, 5, 4, 3, 2, 1},
{4, 1, 2, 3, 6, 7, 8, 5}, {1, 7, 2, 4, 5, 3, 6, 8} } ;
void do_trans( int * inboard, int * outboard, int t)
{ /* calculate the board (into outboard) that results from doing
the t'th transformation to inboard */
int lv;
int *tform = tforms[t];
assert(t >= 0 && t < 3);
for (lv = 0; lv < 8; lv++)
outboard[lv] = inboard[tform[lv]-1];
}
void do_rtrans( int * inboard, int * outboard, int t)
{ /* calculate the board (into outboard) that which would result
in inboard if the t'th transformation was applied to it */
int lv;
int *tform = tforms[t];
assert(t >= 0 && t < 3);
for (lv = 0; lv < 8; lv++)
outboard[tform[lv]-1] = inboard[lv];
}
/* queue for breadth-first search */
int queue[ 40325 ][ 8 ];
int qhead, qtail;
/* calculate the distance from each board to the ending board */
void do_dist( int * board)
{
int lv;
int t1;
int d, t;
qhead = 0;
qtail = 1;
/* the ending board is 0 steps away from itself */
for (lv = 0; lv < 8; lv++) queue[0][lv] = board[lv];
dist[encode(queue[0])] = 1; /* 0 steps (+ 1 offset for dist array) */
while (qhead < qtail)
{
t1 = encode(queue[qhead]);
d = dist[t1];
/* for each transformation */
for (lv = 0; lv < 3; lv++)
{
/* apply the reverse transformation */
do_rtrans(queue[qhead], queue[qtail], lv);
t = encode(queue[qtail]);
if (dist[t] == 0)
{ /* found a new board position! add it to queue */
qtail++;
dist[t] = d+1;
}
}
qhead++;
}
}
/* find the path from the initial configuration to the ending board */
void walk(FILE * fout)
{
int newboard[8];
int cboard[8];
int lv, lv2;
int t, d;
for (lv = 0; lv < 8; lv++) cboard[lv] = lv;
d = dist[encode(cboard)];
/* start at the ending board */
while (d > 1)
{
for (lv = 0; lv < 3; lv++)
{
do_trans(cboard, newboard, lv);
t = encode(newboard);
if (dist[t] == d-1) /* we found the previous board! */
{
/* output transformatino */
fprintf (fout, "%c", lv+'A');
/* find the rest of the path */
for (lv2 = 0; lv2 < 8; lv2++) cboard[lv2] = newboard[lv2];
break;
}
}
assert(lv < 3);
d--;
}
fprintf (fout, " ");
}
int main( int argc, char ** argv)
{
FILE *fout, *fin;
int board[8];
int lv;
if ((fin = fopen("msquare.in", "r")) == NULL)
{
perror ("fopen fin");
exit(1);
}
if ((fout = fopen("msquare.out", "w")) == NULL)
{
perror ("fopen fout");
exit(1);
}
for (lv = 0; lv < 8; lv++)
{
fscanf (fin, "%d", &board[lv]);
board[lv]--; /* use 0-based instead of 1-based */
}
/* calculate the distance from each board to the ending board */
do_dist(board);
for (lv = 0; lv < 8; lv++) board[lv] = lv;
/* output the distance from and the path from the initial configuration */
fprintf (fout, "%d ", dist[encode(board)]-1);
walk(fout);
return 0;
}