任务是将给定状态转换为最终状态最少的步骤。例如,如果您输入如下字符串:
213456
状态看起来像这样(总是6个字符):
2 1 3
4 5 6
您需要将其转换为的状态始终相同:
1 2 3
4 5 6
您需要切换数字以获得最终状态,您只需水平和垂直切换它们。这不是一项艰巨的任务,但我的永无止境的递归问题。请不要评论代码(使用命名空间std;不使用函数重复进程等),因为我还没有完成,我需要你帮助我理解为什么这是一个永无止境的递归。
编辑:代码现在正在运作!
#include <iostream>
#include <map>
#include <string>
#include <algorithm>
#include <vector>
using namespace std;
int f(string s, map <string, int>& visited, int steps = 0)
{
string s2 = s;
vector <int> solutions;
solutions.push_back(721);
if(s == "123456")
return steps;
else
{
swap(s2[0], s2[1]);
if(visited.find(s2) == visited.end() or steps < visited[s2])
{
visited[s2] = steps;
solutions.push_back(f(s2, visited, steps + 1));
}
s2 = s;
swap(s2[1], s2[2]);
if(visited.find(s2) == visited.end() or steps < visited[s2])
{
visited[s2] = steps;
solutions.push_back(f(s2, visited, steps + 1));
}
s2 = s;
swap(s2[3], s2[4]);
if(visited.find(s2) == visited.end() or steps < visited[s2])
{
visited[s2] = steps;
solutions.push_back(f(s2, visited, steps + 1));
}
s2 = s;
swap(s2[4], s2[5]);
if(visited.find(s2) == visited.end() or steps < visited[s2])
{
visited[s2] = steps;
solutions.push_back(f(s2, visited, steps + 1));
}
s2 = s;
swap(s2[0], s2[3]);
if(visited.find(s2) == visited.end() or steps < visited[s2])
{
visited[s2] = steps;
solutions.push_back(f(s2, visited, steps + 1));
}
s2 = s;
swap(s2[1], s2[4]);
if(visited.find(s2) == visited.end() or steps < visited[s2])
{
visited[s2] = steps;
solutions.push_back(f(s2, visited, steps + 1));
}
s2 = s;
swap(s2[2], s2[5]);
if(visited.find(s2) == visited.end() or steps < visited[s2])
{
visited[s2] = steps;
solutions.push_back(f(s2, visited, steps + 1));
}
return *(min_element(solutions.begin(), solutions.end()));
}
}
int main()
{
string s;
cin >> s;
map <string, int> visited;
cout << f(s, visited) << endl;
return 0;
}
示例输入:
321456
示例输出:
3
答案 0 :(得分:3)
问题是visited
是按值传递的。如果您在递归调用中更改它,一旦返回,您就会失去此更改,就像您没有访问任何内容一样。
尝试通过引用传递的参数:
int f(string s, map <string, bool>& visited, int steps = 0) // & in the dfinition of f
对我来说它返回5.我不知道它的正确值,但递归结束了
修改强>
我已经分析了这种递归算法:
if
对应一个潜在的替代举动。 visited
会累积前一级别中考虑的所有替代项。但不一定是那些动作的成功。这将导致忽略尚未导致解决方案的潜在动作。 visited
。 现在我提出了一个名为修剪的简单技巧:我们将跟踪解决方案中最短的移动次数。每当我们探索时,我们会采取比这条最短路径更多的步骤,我们不会继续搜索。
我已经完成了另外两项更改:首先,我使用循环来探索替代方案,因为我懒得多次复制几乎相同的代码。我确实摆脱了解决方案的载体,只是用另一种方式跟踪最小数量的步骤:
int f(string s, int &sh, map <string, int> visited, int steps = 0)
{
if (steps >= sh) // THIS IS THE KEY: PRUNING !!!
return INT_MAX;
static vector <pair<size_t, size_t>>possible_swaps{ { 0, 1 }, { 1, 2 }, { 2, 3 }, { 4, 5 }, { 0, 3 }, { 1, 4 }, { 2, 5 } }; // yes
if (s == "123456") { // solution found !!
if (sh > steps) // check if it's the shortest solution for later pruning
sh = steps;
return steps; // in any case, return the number of steps found
}
else
{
int smin = INT_MAX; // just keep trace shortest number of steps in this iteration so far
for (pair<size_t, size_t> sw : possible_swaps) { // try possible swaps
string s2 = s;
swap(s2[sw.first], s2[sw.second]);
if (visited.find(s2) == visited.end()) {
map <string, int> testvisited = visited; // attention: we work on a temporary so to keep visited unchanged, for the next loops.
testvisited[s2] = true;
int stp = f(s2, sh, testvisited, steps + 1); // Recursion
if (stp < smin) // if it's the shoretst alternative
smin = stp; // keep track of it
}
}
return smin; // return the shortest nmber of steps in iteration.
}
}
然后在main()
中你只需要调用,使用一个初始化变量,该变量应包含迄今为止在所有递归中找到的最短路径:
...
int shortest = INT_MAX;
cout << "Result: "<<f(s, shortest, visited) << endl;
对于记录,这次我找到了3.
答案 1 :(得分:1)
而不是map <string, bool> visited
,您应该使用map <string, int>& visited
(请注意参考传递)。
然后您可以将if语句更改为:
if(visited.find(s2) == visited.end() || steps < visited[s2])
{
visited[s2] = steps;
solutions.push_back(f(s2, visited, steps + 1));
}
这样可以让你所有深度的路径相互传达一些路径信息。
请注意,您可以通过使用迭代解决方案进一步优化。广度优先搜索,但这应该足以在我们的生活中完成运行:)