1、这是96年的一道IOI题目,一道恶心的搜索。。。最近USACO快做哭了,根本没把我当初学者T T。。。谁说“做完前三章,稳拿一等奖”啊,岂止一等奖,明明可以参加NOI了。。。
2、刚开始我脑子发昏,竟然倒序输出,所以怎么都不对,没法输出字典序最小,还以为是搜索顺序的问题,硬是把程序改复杂了。。。
3、算法嘛,就是bfs+康托展开+逆康托展开即可。注意题目给出的数字顺序和常规不太一样,初始状态应是1,2,3,4,8,7,6,5。。。
4、对换行符要求也很严格,即使是0步,也要输出一个换行符。。。USACO你够了。。。
/*
ID:mrxy564
PROG:msquare
LANG:C++
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=9;
struct state{
char dir;
int parent;
int val;
int num;
};
state s,e,q[41000];
int cnt=0,front,rear,Cnt;
char ans[41000],minans[41000];
bool flag=false,vis[41000];
int factorial[MAXN];
void intToArray(int x,int a[MAXN]){
bool used[MAXN];
int i,j,temp;
for(i=1;i<=8;i++) used[i]=false;
for(i=1;i<=8;i++){
temp=x/factorial[8-i];
for(j=1;j<=8;j++) if(!used[j]){
if(temp==0) break;
temp--;
}
a[i]=j;
used[j]=true;
x%=factorial[8-i];
}
}
int arrayToInt(int a[MAXN]){
int ans,i,j,temp;
ans=0;
for(i=1;i<=8;i++){
temp=a[i]-1;
for(j=1;j<i;j++) if(a[j]<a[i]) temp--;
ans+=factorial[8-i]*temp;
}
return ans;
}
void init(){
factorial[0]=1;
for(int i=1;i<8;i++)
factorial[i]=factorial[i-1]*i;
for(int i=0;i<41000;i++)
minans[i]='C';
int a[MAXN]={0,1,2,3,4,8,7,6,5};
s.val=arrayToInt(a);
for(int i=1;i<=4;i++)
scanf("%d",&a[i]);
for(int i=8;i>=5;i--)
scanf("%d",&a[i]);
e.val=arrayToInt(a);
memset(vis,0,sizeof(vis));
return;
}
void change(int st,char dir,int pa){
int a[MAXN];
intToArray(st,a);
if(dir=='A'){
for(int i=1;i<=4;i++)
swap(a[i],a[i+4]);
}
if(dir=='B'){
int x1=a[1],x2=a[5];
for(int i=4;i>=1;i--){
int temp=i%4+1;
a[temp]=a[i];
a[temp+4]=a[i+4];
}
a[2]=x1;a[6]=x2;
}
if(dir=='C'){
int x3=a[2];
a[2]=a[6];
a[6]=a[7];
a[7]=a[3];
a[3]=x3;
}
int temp=arrayToInt(a);
if(!vis[temp]){
vis[temp]=true;
state news;
news.dir=dir;
news.parent=pa;
news.val=temp;
news.num=++rear;
q[rear]=news;
}
return;
}
void print(state st){
if(st.val!=s.val){
print(q[st.parent]);
ans[cnt++]=q[st.num].dir;
}
}
void bfs(){
front=-1;rear=0;
vis[s.val]=true;
q[0]=s;
while(front<rear&&!flag){
int temp=q[++front].val;
while(temp==e.val&&front<=rear){
e=q[front];
print(e);
if(strcmp(ans,minans)<0){
for(int i=0;i<cnt;i++)
minans[i]=ans[i];
Cnt=cnt;
cnt=0;
}
flag=true;
temp=q[++front].val;
}
if(flag) break;
change(temp,'A',front);
change(temp,'B',front);
change(temp,'C',front);
}
return;
}
int main(){
freopen("msquare.in","r",stdin);
freopen("msquare.out","w",stdout);
init();
bfs();
if(flag){
int num=0;
printf("%d\n",Cnt);
for(int i=0;i<Cnt;i++){
printf("%c",minans[i]);
num++;
if(num%60==0) printf("\n");
}
if(num%60!=0||Cnt==0) printf("\n");
}
return 0;
}
官方题解:
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, "\n"); } 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\n", dist[encode(board)]-1); walk(fout); return 0; }