Jas*_*per 12 c++ algorithm recursion backtracking
我正在审查本地编程竞赛中的编程问题.
你可以在这里下载问题(pdf).这是荷兰语,但图片将有助于理解它.
您收到am*m grid作为输入,包含一些管道和一些缺失点(问号).其余的管道必须放在网格中,以便它们与其他管道连接.
每个管道都用一个字母表示(见第2页的图片).字母'A'的值为1,'B'的值为2,..
我尝试用回溯解决它(它还没有完成工作).但由于网格可能是10x10,因此速度太慢.有人可以提出更好(更快)的解决方案/方法吗?
#include <iostream>
#include <stdio.h>
#include <vector>
using namespace std;
#define sz(a) int((a).size())
#define pb push_back
int m, found;
string letters;
vector<int> done;
vector<string> a;
int ok(char letter, int c, int r)
{
int val = letter - 'A' + 1;
//checking if no side goes outside
if (r == 0 && (val & 1))
return 0;
if (r == m - 1 && (val & 4))
return 0;
if (c == 0 && (val & 8))
return 0;
if (c == m - 1 && (val & 2))
return 0;
//check if the side is connected the other pipe on the grid
if (r > 0 && a[r - 1][c] != '?' && (a[r - 1][c] & 4) && !(val & 1))
return 0;
if (c > 0 && a[r][c - 1] != '?' && (a[r][c - 1] & 2) && !(val & 8))
return 0;
if (r < m - 1 && a[r + 1][c] != '?' && (a[r + 1][c] & 1) && !(val & 4))
return 0;
if (c < m - 1 && a[r][c + 1] != '?' && (a[r][c + 1] & 8) && !(val & 2))
return 0;
return 1;
}
void solve(int num_placed, int pos)
{
if (found) return;
//done
if (num_placed == sz(letters)) {
for (int i = 0; i < m; ++i)
cout << a[i] << endl;
found = 1;
return;
}
int c = pos % m;
int r = pos / m;
if (a[r][c] != '?')
solve(num_placed, pos + 1);
//try all the pipes on this position
for (int i = 0; i < sz(letters); ++i) {
if (!done[i] && ok(letters[i], c, r)) {
a[r][c] = letters[i];
done[i] = 1;
solve(num_placed + 1, pos + 1);
done[i] = 0;
a[r][c] = '?';
}
}
}
int main()
{
freopen("input.txt", "r", stdin);
int n;
cin >> n;
while (n--) {
cin >> m;
cin >> letters;
cout << m << endl;
a.clear();
for (int i = 0; i < m; ++i) {
string line;
cin >> line;
a.pb(line);
}
done = vector<int>(sz(letters), 0);
found = 0;
solve(0, 0);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
原始回复
你是否必须亲自编写所有代码或者是否有兴趣探索其他工具?因为我建议看一下约束传播/线性规划.你已经有很多边界限制 - 外边缘不能有管道,加上内边缘 - 所以我想这会非常有效.此外,约束看起来很简单,所以它应该很容易设置.
我没有足够的经验来提供更多详细信息(虽然如果我有时间在下周我可以在某个时候给它一个去),但如果这种事情很有意思,那么另一个有更多的背景回答我前段时间写的.
ps有趣的问题; 谢谢你发布这个.
[编辑:如果您不能使用其他库,那么您可以自己进行约束传播.还有一篇由norvig撰写的精彩文章,展示了如何为数独做这件事.我强烈建议阅读 - 我认为你会看到如何跨越这些技术,即使它是数独和蟒蛇.
更新回复(2012-04-06 - 更新博客参考;旧评论有错误)
深度优先搜索,其中下一个空单元格用每个可用的一致区块填充,并且一致性检查包括边缘约束(没有边缘管道)和最近邻居,非常有效.我在clojure中有一个未经优化的实现,它将解决0.4ms左右的较小示例(JVM热备后360ms内的1000)和3ms中的较大示例(cedric van goethem报告优化的1ms - 但仍然是OO-java实现,这似乎是合理的).它可以在12秒内解决10x10拼图(没有初始提示的同心圆管).
我还花时间研究一种"智能"方法,该方法跟踪每个单元格的约束,就像上面的norvig论文一样.然后我尝试使用choco.所有这些都在这里的博客文章中有更详细的描述(我在这个答案的更新中确实有更多的细节,但它们是基于错误的代码 - 博客有更多,更好的信息).该源也可供下载.
所有这一切的一般结论是,直接搜索可以达到10x10.之后,更多智能可能有所帮助,但很难确定,因为生成测试用例很难(即使给出了大量的单元格,它们也很容易模糊).