UVA1343(The rotation Game|旋转游戏)=》状态空间分析+迭代加深搜

例题7-12 旋转游戏 UVa1343

题目大意:数字1,2,3都有八个,求出最少的旋转次数使得图形中间八个数相同。旋转规则:对于每一长行或每一长列,每次旋转就是将数据向头的位置移动一位,头上的数放置到尾部。若次数相同,则找出字典序最小旋转次序。

2.解题思路:本题利用迭代加深搜索解决。好久没做这个专题了,感觉这种方法有点力不从心,不会寻找估价函数是硬伤。。。只好学一学别人的代码。

本题要求棋盘中间的8个方格都要是相同的数字。紫书上说是利用状态空间搜索解决,大致模板还是八数码问题的模板。但是写了半天最后WA了,感觉这道题用那个模板写出来会很复杂。最后看别人的代码,才发现大多都是利用迭代加深搜索解决的,代码量也不是很大。因此学习这种方法了。

本题的移动比较复杂,因此应当事先保存A,B,C,D四个方向的位置,后四个方向可以反推出来。同时还要保存要观察的8个格子的位置。接下来,先判断是否就是目标状态,如果不是,进行迭代加深搜索。

首先要明白迭代加深搜索具体是如何操作的:从小到大依次枚举深度上限maxd。对于每一个深度上限,进行dfs,当递归到maxd时,就要判断是否找到了解,如果找到了,就记录或者输出解。不论是否找到了解,都要开始返回,对于找到了解的情况,可以在函数出口处连续返回。

那么如何及时的返回呢?答案就是寻找“估价函数”并“剪枝”。对于本题,可以发现,每次移动最多只能让目标数字多1个,因此可以将min{diff(1),diff(2),diff(3)}作为估价函数。其中diff(i)是目标格子中不等于数字i的个数。这样以来,如果发现最少要移动的步数h()仍然大于maxd-cur,就需要进行剪枝。

需要注意的是,迭代加深搜索必须在解一定存在的情况下使用,否则会产生无限递归。

#include<cstdio>
#include<algorithm>
using namespace std;
int line[8][7]={
  { 0, 2, 6,11,15,20,22},
  { 1, 3, 8,12,17,21,23},
  {10, 9, 8, 7, 6, 5, 4},
  {19,18,17,16,15,14,13},
  {23,21,17,12,8,3,1},
  {22,20,15,11,6,2,0},
  {13,14,15,16,17,18,19},
  {4,5,6,7,8,9,10},
};// 分别保存A~H的各行
const int rev[8] = {5, 4, 7, 6, 1, 0, 3, 2};//保存各行的编号
const int center[8] = {6, 7, 8, 11, 12, 15, 16, 17};

int a[24];
char ans[1000];

bool is_final() {
  for(int i = 0; i < 8; i++)
  if (a[center[i]] != a[center[0]]) return false;
  return true;
}

int diff(int target) {
  int ans = 0;
  for(int i = 0; i < 8; i++)
    if(a[center[i]] != target) ans++;
  return ans;
}
inline int h() {
  return min(min(diff(1), diff(2)), diff(3));
}

inline void move(int i) {
  int tmp = a[line[i][0]];
  for(int j = 0; j < 6; j++) a[line[i][j]] = a[line[i][j+1]];
  a[line[i][6]] = tmp;
}

bool dfs(int d, int maxd) {
  if(is_final()) {
    ans[d] = '\0';
    printf("%s\n", ans);
    return true;
  }
  if(d + h() > maxd) return false;
  for(int i = 0; i < 8; i++) {
    ans[d] = 'A' + i;
    move(i);
    if(dfs(d+1, maxd)) return true;
    move(rev[i]);
  }
  return false;
}
int main() {
  while(scanf("%d", &a[0]) == 1 && a[0]) {
    for(int i = 1; i < 24; i++) scanf("%d", &a[i]);
    for(int i = 0; i < 24; i++) if(!a[i]) return 0;
    if(is_final()) {
      printf("No moves needed\n");
    } else {
      for(int maxd = 1; ; maxd++)
      {
        if(dfs(0, maxd)) break;
      }
    }
    printf("%d\n", a[6]);
  }
  return 0;
}


 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值