【洛谷 P4289】[HAOI2008]移动玩具(搜索)

其实这题可以不用状压。。

提供一种新思路。

我们在读入目标棋盘的时候,把当前位置的数和当前棋盘进行比较,如果不一样,如果当前是\(1\),目标是\(0\),那么我们就把当前位置加入\(needmove\)队列里去,否则加入\(needgot\)队列里去。

然后我们两遍循环,对这两个队列两两匹配,构成一个操作,表示把\(needmove\)里的一个位置上的\(1\)移到\(needgot\)里的一个位置上去,同时保存该操作需要的步数,就是这两个位置的曼哈顿距离。

然后进行\(DFS\),枚举所有操作,若这两个位置都没变成目标状态,那么标记一下,进行这个操作,然后回溯。

还可以加一个最优性剪枝,降低时间复杂度。

这样,总能得到最优的方案。
时间复杂度也比较优秀。
码量也很少。

#include <cstdio>
#define Open(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
#define Close fclose(stdin);fclose(stdout);
int abs(int x){
    return x > 0 ? x : -x;
}
struct Node{
    int from, to, dis;
}e[4000];
int a[5][5], b[5][5];
int needmove[20], needgot[20], nm, ng, num, vism[4000], visg[4000], ans = 2147483647;
inline int read(){
    char ch = getchar();
    while(ch < '0' || ch > '9') ch = getchar();
    return ch - '0';
}
void dfs(int sum, int fi, int last){
    if(sum > ans) return;
    if(fi == nm){ ans = sum; return; }
    for(int i = last; i <= num; ++i)
       if(!vism[e[i].from] && !visg[e[i].to]){
         vism[e[i].from] = 1;
         visg[e[i].to] = 1;
         dfs(sum + e[i].dis, fi + 1, i + 1);
         vism[e[i].from] = 0;  //回溯
         visg[e[i].to] = 0;
       }
}
int main(){
  //Open("move");
  for(int i = 1; i <= 4; ++i)
     for(int j = 1; j <= 4; ++j)
        a[i][j] = read();
  for(int i = 1; i <= 4; ++i) //得到两个队列
     for(int j = 1; j <= 4; ++j){
        b[i][j] = read();
        if(a[i][j] != b[i][j])
          if(a[i][j] == 1)
            needmove[++nm] = i * 10 + j;
          else
            needgot[++ng] = i * 10 + j;
     }
  for(int i = 1; i <= nm; ++i)  //两两匹配记录所有可能的操作
     for(int j = 1; j <= ng; ++j)
        e[++num].from = needmove[i], e[num].to = needgot[j], 
        e[num].dis = abs(needmove[i] / 10 - needgot[j] / 10) + abs(needmove[i] % 10 - needgot[j] % 10);
  dfs(0, 0, 1);
  printf("%d\n", ans);
  //Close;
  return 0;
}

posted @ 2018-09-07 19:43  Qihoo360  阅读(276)  评论(2编辑  收藏  举报
You're powerful!