八皇后问题爬山法实现(C语言)
运行环境VS2019
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <time.h> 4 #include <stdbool.h> 5 // 6 // 编程题 7 // 爬山法(八皇后问题) 8 // 9 10 11 //棋子结构体 12 //typedef struct Chess * Chess; 13 14 int a[64];//a数组中记录了爬山过程中,每次棋盘碰撞次数的最小值 15 int array_count = 0; //array_count是a数组中的计数器 16 int number = 0;//number为爬山次数 17 18 struct Global_Collidecount_min 19 { 20 int mincollidecount[64]; //存放每次搜索后,棋盘上最小碰撞对数。 21 int globalmin = -1; 22 }global_collidecount_min; 23 24 25 26 typedef struct 27 { 28 int posx; 29 int posy; 30 int collidecount; 31 }Coordinate; 32 33 34 typedef struct 35 { 36 int value;//存储需要比较的数值 37 Coordinate position[8];//存储棋盘上的坐标。 38 }Chess; 39 40 Chess chess; 41 42 43 //地图结构体 44 typedef struct 45 { 46 47 int map[8][8]; 48 int collidecount[8][8]; 49 int collidecount2[8][8]; 50 int collidecount3[8][8]; 51 }Map; 52 53 54 //C实现键值对(Map功能) 55 typedef struct 56 { 57 int key_x; 58 int key_y; 59 int value; 60 bool kaiguan; 61 }Pair; 62 63 64 65 66 Pair pair[64]; 67 Map ditu; 68 69 70 71 // 初始化地图 72 void initmap() 73 { 74 for (int x = 0; x < 8; x++) 75 for (int y = 0; y < 8; y++) 76 { 77 ditu.map[x][y] = 0; 78 ditu.collidecount[x][y] = 0; 79 ditu.collidecount2[x][y] = 0; 80 ditu.collidecount3[x][y] = 0; 81 } 82 } 83 84 85 //初始化八皇后,按列赋初值 86 void initchess() 87 { 88 int y; 89 for (y = 0; y < 8; y++) 90 { 91 int x = rand() % 8; 92 chess.value = 1; 93 chess.position[y].posx = x; 94 chess.position[y].posy = y; 95 chess.position[y].collidecount = 0; 96 ditu.map[x][y] = chess.value;//用1表示棋子 97 } 98 99 100 } 101 102 103 //初始化键值对 104 void initpair() 105 { 106 107 for (int i = 0; i < 63; i++) 108 { 109 pair[i].key_x = 0; 110 pair[i].key_y = 0; 111 pair[i].kaiguan = false; 112 pair[i].value = 0; 113 } 114 115 } 116 117 118 119 120 //输出地图 121 void outmap() 122 { 123 124 for (int x = 0; x < 8; x++) 125 { 126 for (int y = 0; y < 8; y++) 127 printf("%3.1d", ditu.map[x][y]); 128 printf("\n"); 129 } 130 } 131 132 133 //输出棋子位置 134 void outchess() 135 { 136 137 for (int x = 0; x < 8; x++) 138 { 139 140 printf("x=%3.1d,y=%3.1d", chess.position[x].posx, chess.position[x].posy); 141 printf("\n"); 142 } 143 144 } 145 146 147 148 void outmap1() 149 { 150 151 for (int x = 0; x < 8; x++) 152 { 153 for (int y = 0; y < 8; y++) 154 printf("%3.1d", ditu.collidecount[x][y]); 155 printf("\n"); 156 } 157 } 158 159 160 void outmap2() 161 { 162 163 for (int x = 0; x < 8; x++) 164 { 165 for (int y = 0; y < 8; y++) 166 printf("%3.1d", ditu.collidecount2[x][y]); 167 printf("\n"); 168 } 169 } 170 171 172 void outmap3() 173 { 174 175 for (int x = 0; x < 8; x++) 176 { 177 for (int y = 0; y < 8; y++) 178 printf("%3.1d", ditu.collidecount3[x][y]); 179 printf("\n"); 180 } 181 } 182 183 184 185 // 将地图中的碰撞数ditu.collidecount,ditu.collidecount2,ditu.collidecount3置0; 186 void resetcollidecount() 187 { 188 for (int x = 0; x < 8; x++) 189 for (int y = 0; y < 8; y++) 190 { 191 ditu.collidecount[x][y] = 0; 192 ditu.collidecount2[x][y] = 0; 193 ditu.collidecount3[x][y] = 0; 194 } 195 } 196 197 198 // 将存储棋盘中最小碰撞数的pair还原 199 void resetpair() 200 { 201 202 for (int i = 0; i < 63; i++) 203 { 204 pair[i].key_x = 0; 205 pair[i].key_y = 0; 206 pair[i].kaiguan = false; 207 pair[i].value = 0; 208 } 209 210 } 211 212 213 /// <summary> 214 /// 将地图中的碰撞数ditu.collidecount,ditu.collidecount2,ditu.collidecount3置0; 215 /// 将存储棋盘中最小碰撞数的pair还原 216 /// </summary> 217 void reset() 218 { 219 resetcollidecount(); 220 resetpair(); 221 } 222 223 224 225 //查看是否与棋盘中皇后位置重复 226 int find(int row, int column) 227 { 228 int m; 229 for (m = 0; m < 8; m++) 230 { 231 int posx = chess.position[m].posx; 232 int posy = chess.position[m].posy; 233 if ((posx == row) && (posy == column)) 234 return 0; 235 } 236 return 1; 237 } 238 239 240 241 //随机选取一个最小碰撞数所在的位置,将地图中同列的皇后置0, 242 void randomselect(int position) 243 { 244 245 srand((int)time(NULL)); 246 int x = rand() % position; 247 /*printf("%d\t%d\n", x, position); 248 printf("%d\n", pair[x].key_y);*/ 249 int posx = chess.position[pair[x].key_y].posx;//取得同列皇后的横坐标 250 int posy = chess.position[pair[x].key_y].posy;//取得同列皇后的纵坐标 251 //printf("%d\t%d\n", posx, posy); 252 chess.position[pair[x].key_y].posx = pair[x].key_x;//将皇后的横坐标该为最小碰撞数所在的位置的横坐标 253 //printf("%d\n", pair[x].key_x); 254 ditu.map[posx][posy] = 0;//将地图中皇后原位置置0 255 ditu.map[pair[x].key_x][pair[x].key_y] = 1;//将地图中皇后新位置置1 256 } 257 258 259 //统计棋盘中最小碰撞数的个数,并将最小碰撞数所在位置和值存到pair键值对中 260 void countmin(int min) 261 { 262 int position = 0; 263 for (int n = 0; n < 8; n++) 264 for (int m = 0; m < 8; m++) 265 if (ditu.collidecount3[n][m] == min) 266 { 267 pair[position].key_x = n; 268 pair[position].key_y = m; 269 pair[position].kaiguan = true; 270 pair[position].value = min; 271 position++; 272 } 273 274 randomselect(position); 275 } 276 277 278 279 280 //遍历 pair[]数组,找出最小碰撞数 281 int visit() 282 { 283 284 int min, min_x, min_y; 285 286 for (min_x = 0; min_x < 8; min_x++) 287 { 288 for (min_y = 0; min_y < 8; min_y++) 289 { 290 if (find(min_x, min_y)) 291 { 292 min = ditu.collidecount3[min_x][min_y]; 293 //printf("%d\n", min); 294 //printf("%d\t%d\n", min_x, min_y); 295 break; 296 } 297 } 298 break; 299 } 300 /*printf("%d\t%d\n", min_x, min_y); 301 printf("%d\n", min);*/ 302 303 304 for (int i = 0; i < 8; i++) 305 for (int j = 0; j < 8; j++) 306 if (find(i, j)) 307 { 308 if (min > ditu.collidecount3[i][j]) 309 min = ditu.collidecount3[i][j]; 310 } 311 //printf("%d\n", min); 312 313 return min; 314 } 315 316 317 318 319 320 int countcollidecount() 321 { 322 int row, column, count = 0; 323 for (row = 0; row < 8; row++) 324 for (column = 0; column < 8; column++) 325 if (ditu.collidecount2[row][column] != 0) 326 count += ditu.collidecount2[row][column]; 327 return count; 328 } 329 330 331 332 /// <summary> 333 /// 对m列的n位置上的棋子进行检测 334 /// </summary> 335 /// <param name="hang"></param> 336 /// <param name="lie"></param> 337 void function3(int hang, int lie) 338 { 339 int collidecount = 0; 340 int collidecount2 = 0; 341 //行检测 342 for (int count = 0; count < 8; count++) 343 { 344 int m, n; 345 int posx = chess.position[count].posx; 346 int posy = chess.position[count].posy; 347 //printf("x=%d,y=%d\n", posx, posy); 348 for (m = 0; m < 8; m++) 349 { 350 if ((ditu.map[posx][m] != 0) && (m != posy)) 351 { 352 collidecount++; 353 354 } 355 else 356 continue; 357 } 358 359 360 361 362 //lie列 363 for (n = 0; n < 8; n++) 364 { 365 if ((ditu.map[n][posy] != 0) && (n != posx)) 366 { 367 collidecount++; 368 } 369 else 370 continue; 371 } 372 373 //dui'jiao'xian对角线 374 375 n = posx - 1; m = posy + 1; 376 { for (; (n != -1) && (m != 8); n--, m++) 377 if (ditu.map[n][m] != 0) 378 collidecount++; 379 } 380 381 n = posx + 1; m = posy - 1; 382 { for (; (n != 8) && (m != -1); n++, m--) 383 if (ditu.map[n][m] != 0) 384 collidecount++; 385 } 386 n = posx - 1; m = posy - 1; 387 388 { for (; (n != -1) && (m != -1); n--, m--) 389 if (ditu.map[n][m] != 0) 390 collidecount++; 391 } 392 393 n = posx + 1; m = posy + 1; 394 { for (; (n != 8) && (m != 8); n++, m++) 395 if (ditu.map[n][m] != 0) 396 collidecount++; 397 ditu.collidecount2[posx][posy] += collidecount; 398 } collidecount = 0; 399 400 401 402 } 403 404 ditu.collidecount3[hang][lie] = countcollidecount(); 405 406 407 //置零 408 for (int n = 0; n < 8; n++) 409 { 410 for (int m = 0; m < 8; m++) 411 { 412 ditu.collidecount2[n][m] = 0; 413 } 414 } 415 416 } 417 418 419 420 /// <summary> 421 /// 检查同列中其他位置上的碰撞数 422 /// </summary> 423 void function2() 424 { 425 for (int n = 0; n < 8; n++) 426 for (int m = 0; m < 8; m++) 427 { 428 429 if (!find(n, m)) 430 continue; 431 else 432 { 433 int posx = chess.position[m].posx; 434 int posy = chess.position[m].posy; 435 ditu.map[posx][posy] = 0;//将第m列的皇后置0 436 ditu.map[n][m] = 1;//将m列的第n个位置变为1 437 chess.position[m].posx = n; 438 chess.position[m].posy = m; 439 outmap(); 440 outmap3(); 441 function3(n, m);//对m列n位置碰撞检测 442 printf("\n"); 443 ditu.map[posx][posy] = 1; //恢复皇后 444 ditu.map[n][m] = 0; 445 chess.position[m].posx = posx; 446 chess.position[m].posy = posy; 447 } 448 } 449 450 } 451 452 453 454 //碰撞对数检测 455 void function1() 456 { 457 int collidecount = 0; 458 459 //行检测 460 for (int count = 0; count < 8; count++) 461 { 462 int m, n; 463 int posx = chess.position[count].posx; 464 int posy = chess.position[count].posy; 465 //printf("x=%d,y=%d\n", posx, posy); 466 for (m = 0; m < 8; m++) 467 { 468 if ((ditu.map[posx][m] != 0) && (m != posy)) 469 { 470 collidecount++; 471 472 } 473 else 474 continue; 475 } 476 477 478 479 //lie列 480 for (n = 0; n < 8; n++) 481 { 482 if ((ditu.map[n][posy] != 0) && (n != posx)) 483 { 484 collidecount++; 485 } 486 else 487 continue; 488 } 489 490 //对角线检测 491 n = posx - 1; m = posy + 1; 492 { for (; (n != -1) && (m != 8); n--, m++) 493 if (ditu.map[n][m] != 0) 494 collidecount++; 495 } 496 497 n = posx + 1; m = posy - 1; 498 { for (; (n != 8) && (m != -1); n++, m--) 499 if (ditu.map[n][m] != 0) 500 collidecount++; 501 } 502 n = posx - 1; m = posy - 1; 503 504 { for (; (n != -1) && (m != -1); n--, m--) 505 if (ditu.map[n][m] != 0) 506 collidecount++; 507 } 508 509 n = posx + 1; m = posy + 1; 510 { for (; (n != 8) && (m != 8); n++, m++) 511 if (ditu.map[n][m] != 0) 512 collidecount++; 513 chess.position[count].collidecount += collidecount; 514 } collidecount = 0; 515 516 } 517 for (int count = 0; count < 8; count++) 518 { 519 int posx = chess.position[count].posx; 520 int posy = chess.position[count].posy; 521 ditu.map[posx][posy] = chess.position[count].collidecount; 522 } 523 524 } 525 526 527 528 529 530 //输出数组 531 void output(int* array) 532 { 533 for (int i = 0; i <= 63; i++) 534 { 535 printf("%4.1d", array[i]); 536 } 537 } 538 539 540 void output_globalcollidecount() 541 { 542 for (int i = 0; i <= 63; i++) 543 { //if(global_collidecount_min.mincollidecount[i]!=-1) 544 printf("%4.1d", global_collidecount_min.mincollidecount[i]); 545 } 546 547 } 548 549 550 551 //初始化碰撞数 552 void init_globalcollidecount() 553 { 554 for (int i = 0; i <= 63; i++) 555 { 556 global_collidecount_min.mincollidecount[i] = -1; 557 } 558 } 559 560 561 562 563 564 //存储棋盘中除皇后位置之外,最小的碰撞数 565 void save_mincollidecount(int global_min) 566 { 567 if (global_collidecount_min.globalmin == -1) 568 { 569 global_collidecount_min.globalmin = global_min; 570 global_collidecount_min.mincollidecount[0] = global_min; 571 } 572 else 573 { 574 575 global_collidecount_min.mincollidecount[number] == global_min; 576 577 578 579 if (global_min < global_collidecount_min.globalmin) 580 global_collidecount_min.globalmin = global_min;//棋盘上,每次检测中的最小碰撞数 581 } 582 } 583 584 585 586 //如果碰撞数不小于之前所有棋盘检测的最小碰撞数,返回0,爬山法终止 587 int find_global_collidecount(int global) 588 { 589 590 591 //global是第一次存入,或者global比global_collidecount_min.globalmin还小 592 if ((global_collidecount_min.globalmin == -1) || (global < global_collidecount_min.globalmin)) 593 { 594 a[array_count++] = global; 595 save_mincollidecount(global); 596 return 1; 597 } 598 else 599 return 0; 600 } 601 602 603 604 605 void HillClimbing() 606 { 607 int min; 608 initmap(); //初始化地图 609 initpair(); //初始化键值对 610 srand((int)time(NULL)); //随机种子 611 initchess(); //初始化棋盘 612 function1(); //碰撞对数检测 613 function2(); //对除皇后外的其他位置进行碰撞检测,将检测值存于ditu.collidecount3 614 min = visit(); //找出ditu.collidecount3中,皇后之外区域内的最小碰撞数 615 616 617 while (find_global_collidecount(min))//如果碰撞数不小于之前所有棋盘检测的最小碰撞数,返回0,爬山法终止 618 { 619 number++; 620 countmin(min); //统计棋盘中最小碰撞数的个数,并将最小碰撞数所在位置和值存到pair键值对中 621 reset(); //将地图中的碰撞数ditu.collidecount,ditu.collidecount2,ditu.collidecount3置0;将存储棋盘中最小碰撞数的pair[]还原(置0) 622 function1(); //碰撞对数检测 623 function2(); // 检查同列中其他位置上的碰撞数 624 min = visit(); //遍历 pair[]数组,找出最小碰撞数 625 } 626 } 627 628 629 630 int main() 631 { 632 init_globalcollidecount();//初始化碰撞数 633 HillClimbing();//爬山法 634 printf("\n"); 635 printf("globalmin=%3.1d\n", global_collidecount_min.globalmin);//输出最终的最小碰撞次数 636 output(a);//a数组中记录了爬山过程中,每次棋盘碰撞次数的最小值 637 printf("\n"); 638 printf("%d", number);//number为爬山次数 639 return 0; 640 }