2005年百度之星程序设计大赛试题总决赛题目
题目描述:
八方块移动游戏要求从一个含 8个数字(用 1-8表示)的方块以及一个空格方块(用 0表示)的 3x3矩阵的起始状态开始,不断移动该空格方块以使其和相邻的方块互换,直至达到所定义的目标状态。空格方块在中间位置时有上、下、左、右 4个方向可移动,在四个角落上有 2个方向可移动,在其他位置上有 3个方向可移动。例如,假设一个 3x3矩阵的初始状态为:
8 0 3
2 1 4
7 6 5
目标状态为:
1 2 3
8 0 4
7 6 5
则一个合法的移动路径为:
8 0 3 8 1 3 8 1 3 0 1 3 1 0 3 1 2 3
2 1 4 => 2 0 4 => 0 2 4 => 8 2 4 => 82 4 => 8 0 4
7 6 5 7 6 5 7 6 5 7 6 5 7 6 5 7 6 5
另外,在所有可能的从初始状态到目标状态的移动路径中,步数最少的路径被称为最短路径;在上面的例子中,最短路径为 5。如果不存在从初试状态到目标状态的任何路径,则称该组状态无解。
请设计有效的(细节请见评分规则)算法找到从八方块的某初试状态到某目标状态的所有可能路径中的最短路径,并用 C/C++实现。
输入数据:
程序需读入已被命名为 start.txt的初始状态和已被命名为 goal.txt的目标状态,这两个文件都由 9个数字组成( 0表示空格, 1-8表示 8个数字方块),每行 3个数字,数字之间用空格隔开。
输出数据:
如果输入数据有解,输出一个表示最短路径的非负的整数;如果输入数据无解,输出 -1。
自测用例:
如果输入为: start.txt和 goal.txt,则产生的输出应为:
5
又例,如果用
7 8 4
3 5 6
1 0 2
替换 start.txt中的内容,则产生的输出应为:
21
评分规则:
1)我们将首先使用和自测用例不同的 10个 start.txt以及相同的 goal.txt,每个测试用例的运行时间在一台 Intel Xeon2.80GHz 4 CPU/ 6G内存的 Linux机器上应不超过 10秒(内存使用不限制),否则该用例不得分;
2)每个选手的总分(精确到小数点后 6位) =10秒钟内能产生正确结果的测试用例数量 x10+( 1/产生这些正确结果的测试用例的平均运行毫秒 );
3)如果按此评分统计仍不能得出总决赛将决出的一、二、三等奖共计九名获奖者,我们将先设 N=2,然后重复下述过程直至产生最高的 9位得分:用随机生成的另外 10个有解的 start.txt再做测试,并对这 10*N个测试用例用 2)中公式重新计算总分, N++。
第一次发博客,希望多多支持
#include<stdio.h> #include<string> #include<queue> #include<map> #include<time.h> using namespace std; int count1 = 0;//统计 int count2 = 0; int star[3][3];//初始图 int goal[3][3];//目标图 int dir[4][2] = {0,1, 0,-1, 1,0, -1,0}; //状态改变规则 struct Status{ //状态 int m[3][3]; //图 int row,col; //0的位置 int step; //当前几步 char key[10]; //close表标记用 int pay; //d代价 }; struct cmp{ //维护open operator()(Status s1,Status s2){ //if(s1.step != s2.step) //return s1.step > s2.step ? true : false; //else{ return s1.pay >= s2.pay ? true : false; //} } }; priority_queue<Status,vector<Status>,cmp> open; //open表 map<string,int> close; //close表 /*计算key值*/ void getKey(int s[3][3], char key[10]){ int i,j; for(i=0;i<10;i++)key[i]=NULL; for(i=0;i<3;i++){ for(j=0;j<3;j++){ key[3*i + j] = '0'+(s[i][j]); } } //printf("##############%s\n",key); } /*计算代价*/ int calPay(int step,int s[3][3]){ int sum = step; int si,sj; int gi,gj; for(si=0;si<3;si++){ for(sj=0;sj<3;sj++){ for(gi=0;gi<3;gi++){ for(gj=0;gj<3;gj++){ if(s[si][sj]==goal[gi][gj]){ int rpay = si > gi ? si-gi : gi-si;//row距离 int cpay = sj > gj ? sj-gj : gj-sj;//col距离 sum = sum + rpay + cpay; } } } } } return sum; } /*bfs初始化*/ void init(){ //起点 Status begin; int i,j; for(i=0;i<3;i++){ for(j=0;j<3;j++){ begin.m[i][j] = star[i][j]; if(star[i][j]==0){//标记0的位置 begin.row=i; begin.col=j; } } } begin.step = 0; getKey(begin.m, begin.key); printf("起点 = %s\n",begin.key); begin.pay = calPay(begin.step,begin.m); open.push(begin); //加入open表 //终点 Status end; for(i=0;i<3;i++){ for(j=0;j<3;j++){ end.m[i][j] = goal[i][j]; } } end.step = INT_MAX; getKey(end.m,end.key); close[end.key] = 2; //标记终点 printf("终点 = %s\n",end.key); } /*宽度搜索*/ int bfs(){ int i,j; init();//初始化起点的终点 /*printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); Status ss = open.top(); open.pop(); for(i=0;i<3;i++){ for(j=0;j<3;j++){ printf("%d ",ss.m[i][j]); }printf("\n"); }*/ while(!open.empty()){ //搜索 Status cs = open.top();//当前扩展 点 open.pop(); close[cs.key] = 1; //标记为已经扩展close int di; for(di=0;di<4;di++){ //move count1++; int torow = cs.row + dir[di][0]; int tocol = cs.col + dir[di][1]; if(torow<0||torow>2 || tocol<0||tocol>2)continue; count2++; //后继节点 Status next; next.step = cs.step + 1; next.row = torow; next.col = tocol; //printf("下个图\n"); for(i=0;i<3;i++){ for(j=0;j<3;j++){ next.m[i][j] = cs.m[i][j]; } } //移动一点,就是两点互换 int tem; tem = next.m[cs.row][cs.col]; next.m[cs.row][cs.col] = next.m[next.row][next.col]; next.m[next.row][next.col] = tem; getKey(next.m, next.key); //printf("下一点key = %s\n",next.key); if(close[next.key]==2)return next.step;//终点 if(close[next.key]==1)continue; //判断next点是否扩展 next.pay = calPay(next.step,next.m); //加入open表 //printf("下一点\n"); open.push(next); } } return -1; } /*八码问题*/ int main(){ clock_t t1 = clock(); //文件读入数据 int i,j; freopen("star.txt","r",stdin); for(i=0;i<3;i++) for(j=0;j<3;j++) scanf("%d",&star[i][j]); freopen("goal.txt","r",stdin); for(i=0;i<3;i++) for(j=0;j<3;j++) scanf("%d",&goal[i][j]); //打印读入数据 for(i=0;i<3;i++){ for(j=0;j<3;j++){ printf("%d ",star[i][j]); }printf("\n"); } printf("----------\n"); for(i=0;i<3;i++){ for(j=0;j<3;j++){ printf("%d ",goal[i][j]); }printf("\n"); } //bfs找到最少步数 int minStep = bfs(); printf("%d\n",minStep); clock_t t2 = clock(); printf("past time = %d\n",(t2-t1)); printf("count1 = %d\n",count1); printf("count2 = %d\n",count2); return 0; }