UVa 1343 The Rotation Game (状态空间搜索 && IDA*)

题意:有个#字型的棋盘,2行2列,一共24个格。

如图:每个格子是1或2或3,一共8个1,8个2,8个3.

有A~H一共8种合法操作,比如A代表把A这一列向上移动一个,最上面的格会补到最下面。

求:使中心8个格子数字一致的最少步骤,要输出具体的操作步骤及最终中心区域的数字。如果有多个解,输出字典序最小的操作步骤。

 

分析 : 还是状态空间的搜索,对象就是一个数字序列,判断中心位置是否一样,可以看出如果使用BFS,每一层还是爆炸,所以使用IDA*,关键还是模拟操作和h函数,这里的h函数是这样定义的,可以看出每一次操作,最多给当前局面添加一个符合要求的数字,那就统计一下中心区域最多的相同数字有多少,然后如果8-h > max_depth - cur_depth的话代表最好的情况下都无法解决,剪枝。模拟操作应该就是很白痴的数组转移赋值了,代码很长,很烦,建议看看刘汝佳的代码。

#include<bits/stdc++.h>
using namespace std;
int Init[25];
int ans_num;
char ans[1000];
bool Is_ok(int *arr)
{
    int temp = arr[7];
    if(temp!=arr[8] || temp!=arr[9] || temp!=arr[12] || temp!=arr[13] || temp!=arr[16] || temp!=arr[17] || temp!=arr[18])
        return false;
    return true;
}

inline int h(const int *now)
{
    int cnt[4];
    memset(cnt, 0, sizeof(cnt));
    cnt[now[7]]++,  cnt[now[8]]++,  cnt[now[9]]++,
    cnt[now[12]]++, cnt[now[13]]++, cnt[now[16]]++,
    cnt[now[17]]++, cnt[now[18]]++;
    int ret = max(cnt[1], cnt[2]);
    ret = max(ret, cnt[3]);
    return ret;
}

inline void Change(int *tmp, int one, int two, int three, int four, int five, int six, int seven)
{
    int index[9];
    index[1] = one, index[2] = two, index[3] = three,
    index[4] = four, index[5] = five, index[6] = six;
    index[7] = seven;
    int temp = tmp[one];//!
    for(int i=1; i<7; i++)
        tmp[index[i]] = tmp[index[i+1]];
    tmp[index[7]] = temp;
}

bool DFS(int *now, int cur_depth, int max_depth, int per_dir)
{
    if(8 - h(now) > max_depth - cur_depth) return false;
    if(cur_depth >= max_depth) return false;//!?
    for(int dir=1; dir<=8; dir++){//!
        int tmp[25];
        if(per_dir!=0){
            if((per_dir==1&&dir==6) || (dir==1&&per_dir==6)) continue;
            else if((per_dir==2&&dir==5) || (dir==2&&per_dir==5)) continue;
            else if((per_dir==3&&dir==8) || (dir==3&&per_dir==8)) continue;
            else if((per_dir==4&&dir==7) || (dir==4&&per_dir==7)) continue;
        }
        for(int i=1; i<25; i++) tmp[i] = now[i];
        int top = cur_depth;
        switch(dir){
            case 1: ans[top]='A';Change(tmp,1,3,7,12,16,21,23);break;
            case 2: ans[top]='B';Change(tmp,2,4,9,13,18,22,24);break;
            case 3: ans[top]='C';Change(tmp,11,10,9,8,7,6,5);break;
            case 4: ans[top]='D';Change(tmp,20,19,18,17,16,15,14);break;
            case 5: ans[top]='E';Change(tmp,24,22,18,13,9,4,2);break;
            case 6: ans[top]='F';Change(tmp,23,21,16,12,7,3,1);break;
            case 7: ans[top]='G';Change(tmp,14,15,16,17,18,19,20);break;
            case 8: ans[top]='H';Change(tmp,5,6,7,8,9,10,11);break;
        }
        if(Is_ok(tmp)){
            ans[top+1] = '\0';
            ans_num = tmp[7];
            return true;
        }
        if(DFS(tmp, cur_depth+1, max_depth, dir)) return true;
    }
    return false;
}
int main(void)
{
    while(~scanf("%d", &Init[1]) && Init[1]){
        for(int i=2; i<=24; i++){
            scanf("%d", &Init[i]);
        }
        if(Is_ok(Init)){
            puts("No moves needed");
            printf("%d\n", Init[7]);
            continue;
        }
        int max_depth = 1;
        while(1){
            if(DFS(Init, 0, max_depth, 0)) break;
            max_depth++;
        }
        puts(ans);
        printf("%d\n", ans_num);
    }
    return 0;
}
View Code

刘汝佳代码:

// UVa1343 The Rotation Game
// Rujia Liu
// This solutions uses IDA* instead of BFS described in the book, because it's shorter 8-)
// It's shorter because no need for lookup tables and "automatically" lexicographically smallest solution.
#include<cstdio>
#include<algorithm>
using namespace std;

/*
      00    01
      02    03
04 05 06 07 08 09 10
      11    12
13 14 15 16 17 18 19
      20    21
      22    23
*/

// lines E~H are computed with the help of rev[]
int line[8][7]={
  { 0, 2, 6,11,15,20,22}, // A
  { 1, 3, 8,12,17,21,23}, // B
  {10, 9, 8, 7, 6, 5, 4}, // C
  {19,18,17,16,15,14,13}, // D
};

const int rev[8] = {5, 4, 7, 6, 1, 0, 3, 2}; // reverse lines of each line

// center squares
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() {
  for(int i = 4; i < 8; i++)
    for(int j = 0; j < 7; j++) line[i][j] = line[rev[i]][6-j];
  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;
}
View Code

 

瞎:遇到这种看起来很烦的题目,还是没有那种敏感性去试想状态空间搜索,一来就是想着如何模拟,然后脑袋一团shit,思路根本没有,所以总结应该很重要了,提供了一个思考的方向在那里,真正应该思考的是如何去实现这道题所对应的模型,而不是乱想。

posted @ 2017-07-18 11:51  qwerity  阅读(184)  评论(0编辑  收藏  举报