Problem p00731 (C++) — buggy submission, verdict: Runtime Error

#include <iostream>
#include <queue>
#include <unordered_set>

struct Action {
  size_t x;
  size_t y;
  size_t cost;
  bool left_foot_on;
};

bool operator>(Action lhs, Action rhs) { return lhs.cost > rhs.cost; }

bool operator==(Action lhs, Action rhs) {
  return lhs.x == rhs.x && lhs.y == rhs.y &&
         lhs.left_foot_on == rhs.left_foot_on;
}

namespace std {
template <> struct hash<Action> {
  using result_type = size_t;

  result_type operator()(const Action &act) const {
    return act.x * 31 + act.y * 12 + act.left_foot_on * 7;
  }
};
} // namespace std

class Climber {
private:
  using candidates_t =
      std::priority_queue<Action, std::vector<Action>, std::greater<Action>>;

  size_t _width;
  size_t _height;
  std::string *_cliff;
  candidates_t _candidates;
  std::unordered_set<Action> _traversed;

  char get(size_t x, size_t y);
  void try_put_candidate(const Action &now, size_t x, size_t y);
  void put_candidate(size_t x, size_t y, size_t cost, bool left_foot_on);

public:
  Climber(size_t width, size_t height);
  void load();
  int climb();
};

Climber::Climber(size_t width, size_t height)
    : _width(width), _height(height) {}

void Climber::load() {
  _cliff = new std::string[_height];
  for (size_t y = 0; y < _height; ++y) {
    std::string line;
    for (size_t x = 0; x < _width; ++x) {
      char a;
      std::cin >> a;
      line += a;
    }
    _cliff[y] = line;
  }
}

int Climber::climb() {
  // ??????????????????S????????????????¶????????¶???????????????????????????????
  for (size_t y = 0; y < _height; ++y) {
    for (size_t x = 0; x < _width; ++x) {
      if (get(x, y) == 'S') {
        put_candidate(x, y, 0, false);
        put_candidate(x, y, 0, true);
      }
    }
  }
  // ???????°???????????????????????????????????????????????????????????????????
  while (!_candidates.empty()) {
    const Action now = _candidates.top();
    if (get(now.x, now.y) == 'T') {
      return now.cost;
    }
    _candidates.pop();
#ifdef _DEBUG
    // std::cerr << "(" << now.x << "," << now.y << ")->" << now.cost << " [" <<
    // now.left_foot_on << "]\\n";
#endif
    if (now.left_foot_on) {
      try_put_candidate(now, now.x + 1, now.y - 2);
      try_put_candidate(now, now.x + 1, now.y - 1);
      try_put_candidate(now, now.x + 1, now.y);
      try_put_candidate(now, now.x + 1, now.y + 1);
      try_put_candidate(now, now.x + 1, now.y + 2);
      try_put_candidate(now, now.x + 2, now.y - 1);
      try_put_candidate(now, now.x + 2, now.y);
      try_put_candidate(now, now.x + 2, now.y + 1);
      try_put_candidate(now, now.x + 3, now.y);
    } else {
      try_put_candidate(now, now.x - 1, now.y - 2);
      try_put_candidate(now, now.x - 1, now.y - 1);
      try_put_candidate(now, now.x - 1, now.y);
      try_put_candidate(now, now.x - 1, now.y + 1);
      try_put_candidate(now, now.x - 1, now.y + 2);
      try_put_candidate(now, now.x - 2, now.y - 1);
      try_put_candidate(now, now.x - 2, now.y);
      try_put_candidate(now, now.x - 2, now.y + 1);
      try_put_candidate(now, now.x - 3, now.y);
    }
  }
  // ????????????????????£???
  return -1;
}

void Climber::try_put_candidate(const Action &now, size_t x, size_t y) {
  char type = get(x, y);
  if (type == 'S' || type == 'X' || type == '\\0') {
    return; // type==S??????????????????????????????????????????????§????????????????????????????????????????????????????
  }
  int additionalCost;
  if (type == 'T') {
    additionalCost = 0; // ????????§??????????????????
  } else {
    additionalCost = type - '0';
  }
  put_candidate(x, y, now.cost + additionalCost, !now.left_foot_on);
}

char Climber::get(size_t x, size_t y) {
  if (x >= _width || y >= _height) {
    return '\\0'; // invalid range
  }
  return _cliff[y][x];
}

void Climber::put_candidate(size_t x, size_t y, size_t cost,
                            bool left_foot_on) {
  Action a{x, y, cost, left_foot_on};
  auto alreadyTraversed = _traversed.find(a);
  if (alreadyTraversed == _traversed.end() || alreadyTraversed->cost > cost) {
    _traversed.emplace(a);
    _candidates.emplace(std::move(a));
  }
}

int main() {
  size_t width, height;
  while (true) {
    std::cin >> width >> height;
    if (width == 0 || height == 0) {
      break;
    }
    Climber climber(width, height);
    climber.load();
    std::cout << climber.climb() << std::endl;
  }
}

Error: 0
(0,5)->0 [0]
(1,5)->0 [0]
(1,5)->0 [1]
(0,5)->0 [1]
(0,4)->1 [1]
(0,3)->1 [1]
(1,4)->1 [0]
(2,5)->2 [0]
(1,3)->2 [0]
(2,4)->2 [0]
(1,4)->3 [1]
(3,5)->3 [0]
(3,4)->4 [0]
(0,2)->4 [1]
(1,3)->4 [1]
(2,5)->5 [1]
(2,4)->5 [1]
(0,1)->6 [1]
(4,4)->7 [0]
(3,1)->8 [0]
(1,1)->8 [0]
(1,0)->8 [0]
(3,4)->11 [1]
(5,4)->12 [0]
(2,1)->12 [0]
(0,0)->12 [1]
(1,0)->12 [1]
(4,2)->12 [0]
(0,9)->0 [0]
(1,9)->0 [0]
(1,9)->0 [1]
(0,9)->0 [1]
(0,8)->1 [1]
(0,7)->1 [1]
(1,7)->1 [0]
(0,5)->2 [1]
(0,6)->2 [1]
(1,4)->3 [0]
(0,3)->4 [1]
(0,2)->4 [1]
(0,4)->4 [1]
(1,0)->5 [0]
(0,9)->0 [0]
(1,9)->0 [0]
(1,9)->0 [1]
(0,9)->0 [1]
(0,8)->1 [1]
(0,7)->1 [1]
(1,7)->1 [0]
(0,5)->2 [1]
(0,6)->2 [1]
(1,4)->3 [0]
(0,3)->4 [1]
(0,2)->4 [1]
(0,4)->4 [1]
(0,9)->0 [0]
(1,9)->0 [0]
(3,9)->0 [0]
(7,9)->0 [0]
(9,9)->0 [1]
(6,9)->0 [1]
(2,9)->0 [1]
(6,9)->0 [0]
(5,9)->0 [1]
(0,9)->0 [1]
(2,9)->0 [0]
(5,9)->0 [0]
(4,9)->0 [1]
(1,9)->0 [1]
(4,9)->0 [0]
(9,9)->0 [0]
(8,9)->0 [1]
(3,9)->0 [1]
(8,9)->0 [0]
(7,9)->0 [1]
(1,8)->3 [1]
(2,7)->3 [1]
(2,7)->3 [0]
(1,8)->3 [0]
(8,8)->5 [0]
(7,7)->5 [0]
(7,7)->5 [1]
(8,8)->5 [1]
(9,7)->5 [0]
(4,8)->6 [1]
(4,7)->6 [1]
(4,8)->6 [0]
(4,7)->6 [0]
(3,6)->7 [0]
(0,8)->8 [1]
(2,6)->8 [0]
(5,7)->8 [1]
(0,7)->8 [1]
(5,7)->8 [0]
(1,6)->9 [1]
(2,8)->9 [1]
(4,6)->9 [0]
(1,7)->9 [0]
(3,8)->9 [0]
(3,7)->9 [0]
(2,8)->9 [0]
(1,7)->9 [1]
(3,8)->9 [1]
(3,7)->9 [1]
(3,6)->10 [1]
(7,6)->10 [1]
(9,6)->10 [0]
(2,6)->11 [1]
(0,6)->11 [1]
(3,5)->12 [0]
(1,5)->12 [1]
(7,6)->13 [0]
(5,6)->13 [1]
(4,6)->14 [1]
(5,6)->14 [0]
(4,5)->14 [1]
(1,6)->14 [0]
(7,5)->15 [1]
(3,5)->15 [1]
(4,5)->15 [0]
(9,5)->15 [0]
(2,4)->16 [1]
(2,5)->16 [1]
(0,5)->16 [1]
(4,4)->16 [0]
(5,4)->17 [0]
(1,4)->17 [1]
(1,5)->17 [0]
(4,3)->18 [0]
(7,5)->18 [0]
(3,4)->18 [1]
(2,5)->18 [0]
(2,4)->18 [0]
(7,4)->20 [1]
(9,4)->20 [0]
(4,3)->20 [1]
(4,4)->20 [1]
(1,4)->20 [0]
(3,4)->20 [0]
(5,4)->21 [1]
(0,4)->22 [1]
(3,3)->22 [1]
(6,2)->22 [0]
(0,6)->0 [0]
(1,6)->0 [0]
(1,6)->0 [1]
(0,6)->0 [1]
(0,5)->2 [1]
(2,6)->2 [0]
(1,5)->2 [0]
(4,6)->2 [0]
(1,4)->2 [0]
(3,5)->2 [0]
(2,5)->3 [0]
(3,4)->3 [1]
(2,4)->3 [0]
(3,6)->3 [0]
(0,4)->3 [1]
(1,3)->4 [0]
(3,5)->4 [1]
(1,5)->4 [1]
(3,4)->4 [0]
(1,4)->4 [1]
(2,6)->4 [1]
(2,5)->5 [1]
(1,3)->5 [1]
(5,6)->5 [0]
(3,6)->5 [1]
(2,4)->5 [1]
(0,1)->5 [1]
(0,2)->5 [1]
(5,4)->5 [0]
(1,2)->5 [0]
(5,5)->5 [0]
(0,3)->5 [1]
(5,3)->5 [0]
(1,2)->5 [1]
(2,3)->5 [1]
(2,3)->6 [0]
(3,3)->6 [0]
(4,4)->6 [0]
(4,3)->6 [0]
(4,2)->6 [0]
(6,4)->6 [0]
(2,2)->7 [0]
(3,3)->7 [1]
(3,2)->7 [0]
(6,6)->7 [0]
(1,1)->7 [0]
(4,6)->7 [1]
(6,5)->7 [0]
(2,0)->7 [0]
(3,1)->7 [0]
(4,5)->7 [0]
(0,0)->7 [1]
(2,2)->7 [1]
(3,2)->7 [1]
(1,1)->8 [1]
(5,6)->8 [1]
(5,4)->8 [1]
(5,3)->8 [1]
(5,2)->8 [1]
(4,4)->8 [1]
(2,1)->8 [0]
(4,3)->8 [1]
(4,1)->8 [1]
(3,1)->8 [1]
(4,2)->8 [1]
(1,0)->8 [0]
(5,5)->9 [1]
(5,1)->9 [0]
(4,0)->9 [0]
(3,0)->9 [1]
(4,5)->9 [1]
(5,2)->9 [0]
(2,0)->9 [1]
(2,1)->9 [1]
(6,0)->10 [0]
(7,3)->10 [0]
(6,2)->10 [0]
(7,5)->10 [0]
(8,6)->10 [0]
(7,6)->10 [0]
(1,0)->10 [1]
(6,3)->10 [0]
(4,1)->10 [0]
(7,4)->10 [0]
(7,1)->10 [0]
(3,0)->10 [0]
(7,2)->10 [0]
(4,0)->11 [1]
(8,3)->11 [0]
(6,1)->11 [0]
(8,2)->11 [0]
(8,4)->11 [0]
(5,0)->11 [0]
(8,5)->12 [0]
(6,6)->12 [1]
(5,1)->12 [1]
(7,5)->12 [1]
(6,0)->12 [1]
(7,4)->12 [1]


Fixed submission:

#include <iostream>
#include <queue>
#include <unordered_set>

struct Action {
  size_t x;
  size_t y;
  size_t cost;
  bool left_foot_on;
};

bool operator>(Action lhs, Action rhs) { return lhs.cost > rhs.cost; }

bool operator==(Action lhs, Action rhs) {
  return lhs.x == rhs.x && lhs.y == rhs.y &&
         lhs.left_foot_on == rhs.left_foot_on;
}

namespace std {
template <> struct hash<Action> {
  using result_type = size_t;

  result_type operator()(const Action &act) const {
    return act.x * 31 + act.y * 12 + act.left_foot_on * 7;
  }
};
} // namespace std

class Climber {
private:
  using candidates_t =
      std::priority_queue<Action, std::vector<Action>, std::greater<Action>>;

  size_t _width;
  size_t _height;
  std::string *_cliff;
  candidates_t _candidates;
  std::unordered_set<Action> _traversed;

  char get(size_t x, size_t y);
  void try_put_candidate(const Action &now, size_t x, size_t y);
  void put_candidate(size_t x, size_t y, size_t cost, bool left_foot_on);

public:
  Climber(size_t width, size_t height);
  void load();
  int climb();
};

Climber::Climber(size_t width, size_t height)
    : _width(width), _height(height) {}

void Climber::load() {
  _cliff = new std::string[_height];
  for (size_t y = 0; y < _height; ++y) {
    std::string line;
    for (size_t x = 0; x < _width; ++x) {
      char a;
      std::cin >> a;
      line += a;
    }
    _cliff[y] = line;
  }
}

int Climber::climb() {
  // ??????????????????S????????????????¶????????¶???????????????????????????????
  for (size_t y = 0; y < _height; ++y) {
    for (size_t x = 0; x < _width; ++x) {
      if (get(x, y) == 'S') {
        put_candidate(x, y, 0, false);
        put_candidate(x, y, 0, true);
      }
    }
  }
  // ???????°???????????????????????????????????????????????????????????????????
  while (!_candidates.empty()) {
    const Action now = _candidates.top();
    if (get(now.x, now.y) == 'T') {
      return now.cost;
    }
    std::cerr << "(" << now.x << "," << now.y << ")->" << now.cost << " ["
              << now.left_foot_on << "]\\n";
    if (now.left_foot_on) {
      try_put_candidate(now, now.x + 1, now.y - 2);
      try_put_candidate(now, now.x + 1, now.y - 1);
      try_put_candidate(now, now.x + 1, now.y);
      try_put_candidate(now, now.x + 1, now.y + 1);
      try_put_candidate(now, now.x + 1, now.y + 2);
      try_put_candidate(now, now.x + 2, now.y - 1);
      try_put_candidate(now, now.x + 2, now.y);
      try_put_candidate(now, now.x + 2, now.y + 1);
      try_put_candidate(now, now.x + 3, now.y);
    } else {
      try_put_candidate(now, now.x - 1, now.y - 2);
      try_put_candidate(now, now.x - 1, now.y - 1);
      try_put_candidate(now, now.x - 1, now.y);
      try_put_candidate(now, now.x - 1, now.y - 1);
      try_put_candidate(now, now.x - 1, now.y - 2);
      try_put_candidate(now, now.x - 2, now.y - 1);
      try_put_candidate(now, now.x - 2, now.y);
      try_put_candidate(now, now.x - 2, now.y + 1);
      try_put_candidate(now, now.x - 3, now.y);
    }
    _candidates.pop();
  }
  // ????????????????????£???
  return -1;
}

void Climber::try_put_candidate(const Action &now, size_t x, size_t y) {
  char type = get(x, y);
  if (type == 'S' || type == 'X' || type == '\\0') {
    return; // type==S??????????????????????????????????????????????§????????????????????????????????????????????????????
  }
  int additionalCost;
  if (type == 'T') {
    additionalCost = 0; // ????????§??????????????????
  } else {
    additionalCost = type - '0';
  }
  put_candidate(x, y, now.cost + additionalCost, !now.left_foot_on);
}

char Climber::get(size_t x, size_t y) {
  if (x >= _width || y >= _height) {
    return '\\0'; // invalid range
  }
  return _cliff[y][x];
}

void Climber::put_candidate(size_t x, size_t y, size_t cost,
                            bool left_foot_on) {
  Action a{x, y, cost, left_foot_on};
  auto alreadyTraversed = _traversed.find(a);
  if (alreadyTraversed == _traversed.end() || alreadyTraversed->cost > cost) {
    _traversed.emplace(a);
    _candidates.emplace(std::move(a));
  }
}

int main() {
  size_t width, height;
  while (true) {
    std::cin >> width >> height;
    if (width == 0 || height == 0) {
      break;
    }
    Climber climber(width, height);
    climber.load();
    std::cout << climber.climb() << std::endl;
  }
}