AOJ 0121: Seven Puzzle (BFS DP STL 逆向推理)(转载)

转载自:  http://m.blog.csdn.net/blog/Enjoying_Science/42008801

题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=49880

题意:7数码问题。在2×4的棋盘上,摆有7个棋子,每个棋子上标有1至7的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格(用0表示),与空格相邻(上下左右)的棋子可以移到空格中,该棋子原先位置成为空格。给出一个初始状态(保证可以转移到最终状态),找出一种从初始状态转变成给定最终状态的移动棋子步数最少的移动步骤。

输入:多组输入,每组8个数,表示初始状态前四个数为第一行从左到右,后四个数为第二行从左到右。

输出:至少需要多少步可以从输入状态到达最终状态(0 1 2 3 4 5 6 7)

(图1)
  (图2)
  (图3)
分析:
乍一看这题没从入手,但只要采取逆向思维,还是可以用广度优先搜索解决。先不考虑如何用最小步数从输入状态到达最终状态,所有结果的最终状态都是(01234567),那么反过来想,只要求出最终状态到达所有结果时的最小步数并记录下来,接下来就是查表了。0表示空位置,对空位置周围的格子采用广度优先的方式移动到0,并记录下最小步数的结果即可。如上图所示,图1,可以选择让7移动过来变成图2,也可以选择让2移动过来变成图3。我们要做的只不过是不断重复这种选择,直至穷举所有情况并记录结果。
我主要是用一个map<string, int>来表示(01234567)到string 的最小步数int,只要当前结果还不存在,就加入map,必然能够穷尽状态。另外,在移动格子问题上,因为我采用string来表示当前状态,那么移动方向上下左右分别就是当前位置-4, +4, -1, +1。需要注意的是,位置3不能移动到位置4.

思路:与前几题的bfs不同,这次的bfs没有明确的移动对象,看似任意一个数都可以当成对象移动。这时我们只需要抓住一个格子就行,比如我们把0作为移动对象,那么0在地图中漫游所有的格子得到的肯定就是问题的解空间。由于题目的输入是多个case,如果对每个case都运行一遍bfs就会TLE。这时我们祭出dp技能,只需要一次bfs就将解空间算出来,以后每个case只要到解空间中去找就行了。

 1 #include <iostream>
 2 #include <string>
 3 #include <algorithm>
 4 #include <map>
 5 #include <queue>
 6 using namespace std;
 7
 8 map<string, int> dp;
 9 int direction[4] = { 1, -1, 4, -4 };
10
11 //************************************
12 // Method:    bfs
13 // FullName:  bfs
14 // Access:    public
15 // Returns:   void
16 // Qualifier: 让0漫游整个字串
17 //************************************
18 void bfs()
19 {
20     queue<string> que;
21     que.push("01234567");
22     dp["01234567"] = 0;
23     while (!que.empty())
24     {
25         string now = que.front(); que.pop();
26         // p是‘0‘的位置
27         int p = 0;
28         for (int j = 0; j < 8; ++j)
29         {
30             if (now[j] == ‘0‘)
31             {
32                 p = j;
33                 break;
34             }
35         }
36
37         for (int i = 0; i < 4; ++i)
38         {
39             int n = p + direction[i];
40             if (0 <= n && n < 8 &&
41                 !(p == 3 && i == 0) && // 右上角不能再往右了
42                 !(p == 4 && i == 1))   // 左下角不能再往左了
43             {
44                 string next = now;
45                 swap(next[p], next[n]);
46                 if (dp.find(next) == dp.end())
47                 {
48                     dp[next] = dp[now] + 1;
49                     que.push(next);
50                 }
51             }
52         }
53     }
54 }
55
56 ///////////////////////////SubMain//////////////////////////////////
57 int main(int argc, char *argv[])
58 {
59
60     bfs();
61     string line;
62     while (getline(cin, line))
63     {
64         line.erase(remove(line.begin(), line.end(), ‘ ‘), line.end());
65         cout << dp[line] << endl;
66     }
67
68     return 0;
69 }
70 ///////////////////////////End Sub//////////////////////////////////

 1 #include <iostream>
 2 #include <queue>
 3 #include <map>
 4 #include <string>
 5 #include <algorithm>
 6
 7 using namespace std;
 8
 9 typedef pair<string, int> P;
10
11 const int INF = 100000000;
12
13 //输入
14 string a;
15
16 //移动方向
17 int op[4] = {-1, 1, -4, 4};
18
19 map<string, int> dp;                        //保存从string变到"01234567"的int
20
21 //计算从"01234567"转换到其他序列所需的最小步数
22 void bfs(){
23     //初始化
24     queue<P> que;
25     que.push(P("01234567", 0));
26     dp["01234567"] = 0;
27     //宽度优先搜索
28     while(!que.empty()){
29         P p = que.front();
30         que.pop();
31         string s = p.first;
32         int cur = p.second;
33         for(int i = 0; i < 4; i ++){
34             //构造下一次交换
35             int next = cur + op[i];
36             string str = s;
37             swap(str[cur], str[next]);
38             map<string, int>::iterator it = dp.find(str);
39             //判断是否可移动以及是否访问过
40             if(0 <= next && next < 8
41                 && !(cur == 3 && next == 4) && !(cur == 4 && next == 3)
42                 && it == dp.end()){
43
44                 que.push(P(str, next));
45                 dp[str] = dp[s] + 1;
46             }
47         }
48     }
49 }
50
51 void solve(){
52     //删除空格
53     a.erase(remove(a.begin(), a.end(), ‘ ‘), a.end());
54     cout<<dp[a]<<endl;
55 }
56
57 int main(int argc, char const *argv[]){
58     //先逆向构造所有情况,后直接读取输入用例的结果
59     bfs();
60     while(getline(cin, a)){
61         solve();
62     }
63     return 0;
64 }

用find函数来定位数据出现位置,它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器

时间: 2024-10-27 05:52:07

AOJ 0121: Seven Puzzle (BFS DP STL 逆向推理)(转载)的相关文章

AOJ 0121: Seven Puzzle【BFS】

From: AOJ 0121 思路:与前几题的bfs不同,这次的bfs没有明确的移动对象,看似任意一个数都可以当成对象移动.这时我们只需要抓住一个格子就行,比如我们把0作为移动对象,那么0在地图中漫游所有的格子得到的肯定就是问题的解空间.由于题目的输入是多个case,如果对每个case都运行一遍bfs就会TLE.这时我们祭出dp技能,只需要一次bfs就将解空间算出来,以后每个case只要到解空间中去找就行了. #include <iostream> #include <string>

AOJ 0121 Seven Puzzle(反向BFS+map)

卡了很久的一道题,之前用正向BFS硬缸,结果凉凉,下面是之前错误的代码,仅供自己记录,他人浏览请直接跳过: 1 #include<cstring> 2 #include<iostream> 3 #include<queue> 4 #include<algorithm> 5 #include<cstdio> 6 7 using namespace std; 8 9 int tmaze[2][4]; 10 struct node{ 11 int ma

AOJ 0121 Seven Puzzle

7 パズル 7 パズルは 8 つの正方形のカードとこれらのカードがぴたりと収まる枠で構成されています.それぞれのカードには.互いに区別できるように 0, 1, 2, ..., 7 と番号がつけられています.枠には.縦に 2 個.横に 4 個のカードを並べることができます. 7 パズルを始めるときには.まず枠にすべてのカードを入れます.枠のなかで 0 のカードだけは.上下左右に隣接するカードと位置を交換することができます.たとえば.枠の状態が図(a) のときに.0 のカードの右に隣接した.7 のカ

AOJ 0121 Seven Puzzle {广度优先搜索}(*)

原题 题意 题意是有一个输入,比如: 1 0 2 3 4 5 6 7 摆成如下形状: 1 0 2 3 4 5 6 7 0表示空格,其他数字可以移动到0的位置.最后需要到如下形状: 0 1 2 3 4 5 6 7 上面的这种情况是需要移动一步,也就是0和1直接移动就好. 代码 #include<iostream> #include<string> #include<algorithm> #include<queue> #include<map> u

ZOJ3541:The Last Puzzle(区间DP)

There is one last gate between the hero and the dragon. But opening the gate isn't an easy task. There were n buttons list in a straight line in front of the gate and each with an integer on it. Like other puzzles the hero had solved before, if all b

HDU 1072 Nightmare( 身上带有定时炸弹的他能否在炸弹爆炸之前离开—— BFS+DP思想)

Nightmare Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Description Ignatius had a nightmare last night. He found himself in a labyrinth with a time bomb on him. The labyrinth has an exit, Ignatius should get out of the

ZOJ 3596Digit Number(BFS+DP)

一道比较不错的BFS+DP题目 题意很简单,就是问一个刚好包含m(m<=10)个不同数字的n的最小倍数. 很明显如果直接枚举每一位是什么这样的话显然复杂度是没有上限的,所以需要找到一个状态表示方法: 令F[i][j] 表示已经用了 i (二进制压位表示)用了 i 这些数字,且余数j为的状态,枚举时直接枚举当前位,那么答案明显就是F[m][0] 我这里将状态i, j存在了一维空间里,即 i * 1000 + j表示,实际上用一个结构体存队列里的点,用二维数组标记状态也是可行的. 1 #includ

ACM/ICPC 之 最短路-Floyd+SPFA(BFS)+DP(ZOJ1232)

这是一道非常好的题目,融合了很多知识点. ZOJ1232-Adventrue of Super Mario 这一题折磨我挺长时间的,不过最后做出来非常开心啊,哇咔咔咔 题意就不累述了,注释有写,难点在于状态转移方程的确立和SPFA的过程 1 //最短路:Floyd+SPFA(BFS)+DP 2 //Time:20Ms Memory:336K 3 //题目很好,数据较弱,网上部分代码有些问题却能够A掉 4 //题意:超级马里奥要从A+B处背着公主以最短路程到达1处,其中1-A是村庄,剩下的是城堡

【2019.8.14 慈溪模拟赛 T1】我不是!我没有!别瞎说啊!(notme)(BFS+DP)

\(IDA^*\) 说实话,这道题我一开始没想出正解,于是写了一个\(IDA^*\)... 但神奇的是,这个\(IDA^*\)居然连字符串长度分别为\(2500,4000\)的数据都跑得飞快,不过数据发下来之后我测了一下只有45分. 就在不断优化\(IDA^*\)的过程中,我突然就想出了正解的做法,看来以后遇事不决先暴力. \(DP\)求解第一个询问 考虑一个\(DP\),我们设\(f_{i,j}\)表示当前在第一个字符串中是第\(i\)位,第二个字符串中是第\(j\)位的最小步数. 若记录\(