题目大意
给定一个由数字组成的#字型网格,和一定的移动规则,问最少需要多少次移动才能达到要求的结果。
题目分析
要求最少需要几步到达结果,可以考虑广度优先搜索算法,或者迭代加深深度优先搜索(IDA*),这里使用IDA*算法。
在剪枝的时候:
1. 考虑估价函数H(),表示当前中间八个格子中最少需要移动多少次才能相同(即8 -
相同数字最多的个数);
2. 前后两次移动不同为逆操作(即同一列,但不同方向)
3. 不能连续7次进行移动同一列,且同一方向
详细见 CanCut 函数。
实现(c++)
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #define MAX(a, b) a>b? a :b #define MAX_STEP 1000 int gOrderMoved[MAX_STEP]; int gIrreleventQueuee[4] = { 1, 0, 3, 2 }; int gCell[25]; int gCellIndexInQueue[4][7]; int gMoveOrder[4][2] = { { 0, 5 }, { 1, 4 }, { 7, 2 }, { 6, 3 } }; int gMoveQueue[8] = {0, 1, 2, 3, 1, 0, 3, 2}; int gMoveDir[8] = {0, 0, 1, 1, 1, 1, 0, 0}; int gCenterCellIndex[8] = { 6, 7, 8, 11, 12, 15, 16, 17 }; int gMinStep; void Init(){ gCellIndexInQueue[0][0] = 0; gCellIndexInQueue[0][1] = 2; gCellIndexInQueue[0][2] = 6; gCellIndexInQueue[0][3] = 11; gCellIndexInQueue[0][4] = 15; gCellIndexInQueue[0][5] = 20; gCellIndexInQueue[0][6] = 22; gCellIndexInQueue[1][0] = 1; gCellIndexInQueue[1][1] = 3; gCellIndexInQueue[1][2] = 8; gCellIndexInQueue[1][3] = 12; gCellIndexInQueue[1][4] = 17; gCellIndexInQueue[1][5] = 21; gCellIndexInQueue[1][6] = 23; for (int i = 0; i < 7; i++){ gCellIndexInQueue[2][i] = 4 + i; } for (int i = 0; i < 7; i++){ gCellIndexInQueue[3][i] = 13 + i; } } bool IsCenterSame(){ int num = gCell[6]; for (int i = 0; i < 8; i++){ if (gCell[gCenterCellIndex[i]] != num){ return false; } } return true; } void Rotate(int order){ int move_queue = gMoveQueue[order]; int move_dir = gMoveDir[order]; int tmp; if (move_dir == 0){ tmp = gCell[gCellIndexInQueue[move_queue][0]]; for (int i = 0; i < 6; i++) gCell[gCellIndexInQueue[move_queue][i]] = gCell[gCellIndexInQueue[move_queue][i + 1]]; gCell[gCellIndexInQueue[move_queue][6]] = tmp; } else{ tmp = gCell[gCellIndexInQueue[move_queue][6]]; for (int i = 6; i > 0; i--) gCell[gCellIndexInQueue[move_queue][i]] = gCell[gCellIndexInQueue[move_queue][i - 1]]; gCell[gCellIndexInQueue[move_queue][0]] = tmp; } } int GetH(){ int count[4] = { 0, 0, 0, 0 }; for (int i = 0; i < 8; i++){ count[gCell[gCenterCellIndex[i]]] ++; } int max = MAX(count[1], count[2]); max = MAX(max, count[3]); return 8 - max; } bool CanCut(int moved_step, int next_order){ if (moved_step == 0){ return false; } int k = moved_step - 1; int next_queue = gMoveQueue[next_order]; int next_dir = gMoveDir[next_order]; int irrelevent_queue = gIrreleventQueuee[next_queue]; int same_order_count = 0; while (k >= 0){ if (same_order_count >= 6) return true; while (k >= 0 && gMoveQueue[gOrderMoved[k]] == irrelevent_queue){ k--; } if (k < 0){ return false; } int queue = gMoveQueue[gOrderMoved[k]]; int dir = gMoveDir[gOrderMoved[k]]; if (queue == next_queue){ if (dir != next_dir){ return true; } else{ same_order_count++; } } else{ return false; } k--; } return false; } void MoveToCenterSame(int moved_step, bool *moved_same){ if (*moved_same){ return; } if (IsCenterSame()){ for (int i = 0; i < gMinStep; i++){ printf("%c", 'A' + gOrderMoved[i]); } if (gMinStep == 0){ printf("No moves needed"); } printf(" "); printf("%d ", gCell[6]); *moved_same = true; return; } if (moved_step + GetH() > gMinStep){ return; } for (int next_order = 0; next_order < 8; next_order++){ if (CanCut(moved_step, next_order)){ continue; } if (*moved_same){ return; } gOrderMoved[moved_step] = next_order; Rotate(next_order); MoveToCenterSame(moved_step + 1, moved_same); int reback_order = gMoveOrder[gMoveQueue[next_order]][! gMoveDir[next_order]]; Rotate(reback_order); } } void Solve(){ gMinStep = 0; while (true){ bool moved_same = false; MoveToCenterSame(0, &moved_same); if (moved_same){ break; } gMinStep++; } } int main(){ Init(); while (true){ scanf("%d", gCell); if (gCell[0] == 0){ break; } for (int i = 1; i < 24; i++){ scanf("%d", gCell + i); } Solve(); } return 0; }