跟进这个问题:10种人开放卡蒂斯问题的时间限制超过C++
解决了那个问题中的谜题。
我使用Dijkstra算法查找项目。唯一的问题是,每次进行搜索时,我都会将搜索的边界列表保存在Zone中。在随后的搜索中,如果我的当前“区域”碰到一个现有区域,则合并边界列表并使旧区域指向当前区域。
搜索的方块直接存储在地图0或1的平均值上,而任何值>= 100都表示一个区域号(减去100以得到该区域)。然后,您可以在zoneMap中使用此值(如果区域clide使映射保持最新日期)来获取它所属的区域。
我尝试了A*,但是对于这样小的问题空间,它会增加一倍的时间,因为您需要保留一个有序列表来知道下一步要搜索哪一项。您可以看到A*类中最让人想起的部分是注释掉的部分,以保持边界列表的排序。
#include
#include
#include
#include
#include
#include
struct Point: public std::pair
{
friend std::istream& operator>>(std::istream& str, Point& dst)
{
return str >> dst.first >> dst.second;
}
friend std::ostream& operator<<(std::ostream& str, Point const& src)
{
return str << "[" << src.first << "," << src.second << "] ";
}
};
class Zone
{
Point dst;
char type;
int id;
std::vector boundry;
int distsq(Point const& p) const
{
int x = std::abs(p.first - dst.first);
int y = std::abs(p.second - dst.second);
return x * x + y * y;
}
bool order(Point const& lhs, Point const& rhs) const
{
return distsq(lhs) > distsq(rhs);
}
public:
Zone(char type, int id, Point dst)
: type(type)
, id(id)
, dst(dst)
{}
char getType() const {return type;}
int getId() const {return id;}
void updateDestination(Point const& d)
{
using namespace std::placeholders;
dst = d;
//std::make_heap(std::begin(boundry), std::end(boundry), std::bind(&Zone::order, this, _1, _2));
}
bool empty() const {return boundry.empty();}
void push(Point const& p)
{
using namespace std::placeholders;
boundry.emplace_back(p);
//std::push_heap(std::begin(boundry), std::end(boundry), std::bind(&Zone::order, this, _1, _2));
}
void pop()
{
using namespace std::placeholders;
//std::pop_heap(std::begin(boundry), std::end(boundry), std::bind(&Zone::order, this, _1, _2));
boundry.pop_back();
}
Point top() {return boundry./*front*/back();}
void addZoneBoundry(Zone const& src)
{
boundry.reserve(boundry.size() + src.boundry.size());
using namespace std::placeholders;
for (auto const& p: src.boundry) {
boundry.emplace_back(p);
//std::push_heap(std::begin(boundry), std::end(boundry), std::bind(&Zone::order, this, _1, _2));
}
}
};
class Maze
{
std::vector> maze;
std::vector zoneInfo;
std::map zoneMap;
public:
Maze()
{
zoneInfo.reserve(1000);
}
void clear()
{
maze.clear();
zoneInfo.clear();
zoneMap.clear();
}
std::istream& read(std::istream& str)
{
clear();
int r;
int c;
str >> r >> c;
str.ignore(-1, '\n');
maze.resize(r);
std::string line;
for(int loopY = 0; loopY < r; ++loopY)
{
maze[loopY].resize(c);
for(int loopX = 0; loopX < c; ++loopX)
{
char v;
str >> v;
maze[loopY][loopX] = v - '0';
}
}
return str;
}
int const& loc(Point const& point) const {return maze[point.first - 1][point.second - 1];}
int& loc(Point const& point) {return maze[point.first - 1][point.second - 1];}
char type(Point const& point) const
{
int l = loc(point);
if (l < 100) {
return l + '0';
}
return zoneInfo[zone(point)].getType();
}
int zone(Point const& point) const
{
int l = loc(point);
if (l < 100) {
return -1;
}
auto find = zoneMap.find(l - 100);
return find->second;
}
Zone& getCurrentZone(Point const& point, Point const& dst)
{
int l = loc(point);
if (l >= 100) {
l = zoneMap[l - 100];
zoneInfo[l].updateDestination(dst);
return zoneInfo[l];
}
zoneMap[zoneInfo.size()] = zoneInfo.size();
zoneInfo.emplace_back(type(point), zoneInfo.size(), dst);
Zone& cZ = zoneInfo.back();
loc(point) = cZ.getId() + 100;
cZ.push(point);
return cZ;
}
void tryAdding(Zone& cZ, Point const& next, int v, int h)
{
Point point = next;
point.first += v;
point.second += h;
if (point.first <= 0 || point.first > maze.size() ||
point.second <= 0 || point.second > maze[0].size() ||
type(point) != cZ.getType())
{
return;
}
int l = loc(point);
if (l < 100)
{
loc(point) = cZ.getId() + 100;
cZ.push(point);
}
else
{
int currentDest = zoneMap[l - 100];
if (currentDest != cZ.getId())
{
for(auto& item: zoneMap) {
if (item.second == currentDest) {
item.second = cZ.getId();
}
}
cZ.addZoneBoundry(zoneInfo[currentDest]);
}
}
}
// Basically Dijkstra algorithm,
// Returns '0' '1' if the src and dst are the same type and can be reached.
// returns another letter for a failure to connect.
char route(Point const& src, Point& dst)
{
// The zone contains the boundry list.
// If the src already exists in a searched zone then
// re-use the zone and boundary list so we don't have
// to repeat any work.
Zone& cZ = getCurrentZone(src, dst);
// Different types immediately fails.
if (type(dst) != cZ.getType()) {
return 'F';
}
// If we know that both points are in the same zone.
// We don't need to expand the boundary and simply return.
if (zone(dst) == cZ.getId()) {
return cZ.getType();
}
// Otherwise expand the boundary until both
// points are in the zone or we can't expand anymore.
while(!cZ.empty())
{
Point next = cZ.top();
if (next == dst) {
// next location is the destination we have
// confirmed we can get from source to dest.
return cZ.getType();
}
// Only remove next if we are going to expand.
cZ.pop();
tryAdding(cZ, next, -1, 0);
tryAdding(cZ, next, +1, 0);
tryAdding(cZ, next, 0, -1);
tryAdding(cZ, next, 0, +1);
// This extra check is needed because
// zones may have been combined. Thus it checks
// to see if the two points are now in the same zone
// after combining zones.
if (zone(dst) == cZ.getId()) {
return cZ.getType();
}
}
return 'F';
}
friend std::istream& operator>>(std::istream& str, Maze& dst)
{
return dst.read(str);
}
};
int main()
{
Maze maze;
std::cin >> maze;
int count;
std::cin >> count;
Point src;
Point dst;
for(int loop = 0;loop < count; ++loop)
{
std::cin >> src >> dst;
switch (maze.route(src, dst))
{
case '0': std::cout << "binary\n";break;
case '1': std::cout << "decimal\n";break;
default:
std::cout << "neither\n";
}
}
}发布于 2020-09-23 19:44:32
所以从一个小小的角度看,这看起来很好
我本可以采用联合发现结构,但我认为把它存储在地图上是个不错的主意。
有一些事情我想改进:
x和y都可以是const。第二,名字太短了。distance_x或其他什么会更好,第三,你不需要打电话给std::abs,因为负号会互相抵消。第四,你的点结构很便宜,所以我建议把它按价值传递。如果以后需要使用其他类型的话,这会带来一丝麻烦。[] int distsq(Point p) const { const int distance_x = p.first - dst.first;const int distance_y = p.second - dst.second;返回distance_x * distance_x + distance_y * distance_y;}需要走了,以后再回来
编辑
我说我喜欢带区的方法。但是,我相信您最好将区域存储在地图本身中。
根据问题陈述,网格的最大大小是1000×1000。这意味着有最多1,000,000个可能的区域。
使用无符号整数并在MSB中编码映射,然后可以将区域的索引存储在31位较低的位中。因此,在每次启动时,您都可以使用一个新的区域,并通过一个联合查找数据结构将它们合并。
https://codereview.stackexchange.com/questions/249764
复制相似问题