You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
162 lines
4.5 KiB
162 lines
4.5 KiB
2 years ago
|
#include <iostream>
|
||
|
#include <vector>
|
||
|
#include <complex>
|
||
|
#include <deque>
|
||
|
|
||
|
//#define ANIMATE
|
||
|
|
||
|
using namespace std;
|
||
|
|
||
|
struct Input {
|
||
|
vector<vector<unsigned char>> map;
|
||
|
unsigned width;
|
||
|
unsigned height;
|
||
|
complex<int> start;
|
||
|
complex<int> end;
|
||
|
};
|
||
|
|
||
|
complex<int> dirs[] = {
|
||
|
complex<int>(1, 0),
|
||
|
complex<int>(0, 1),
|
||
|
complex<int>(-1, 0),
|
||
|
complex<int>(0, -1)
|
||
|
};
|
||
|
|
||
|
Input parse()
|
||
|
{
|
||
|
Input data;
|
||
|
auto &map = data.map;
|
||
|
for (string line; getline(cin, line);) {
|
||
|
vector<unsigned char> row;
|
||
|
for (auto x : line) {
|
||
|
switch (x) {
|
||
|
case 'S':
|
||
|
data.start = complex<int>(row.size(), map.size());
|
||
|
row.push_back('a' - 'a');
|
||
|
break;
|
||
|
case 'E':
|
||
|
data.end = complex<int>(row.size(), map.size());
|
||
|
row.push_back('z' - 'a');
|
||
|
break;
|
||
|
default: row.push_back(x - 'a'); break;
|
||
|
}
|
||
|
}
|
||
|
data.map.push_back(row);
|
||
|
}
|
||
|
data.width = map.front().size();
|
||
|
data.height = map.size();
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
#ifdef ANIMATE
|
||
|
#include <fstream>
|
||
|
unsigned render_frame = 0;
|
||
|
void render(const Input &input, vector<vector<unsigned>> &dist, deque<complex<int>> &scan)
|
||
|
{
|
||
|
auto scan_map = (bool (*)[input.width])new bool[input.width * input.height]();
|
||
|
for (auto &x : scan) {
|
||
|
scan_map[x.imag()][x.real()] = true;
|
||
|
}
|
||
|
|
||
|
char fname[] = "img/00000.pam";
|
||
|
sprintf(fname, "img/%05u.pam", render_frame++);
|
||
|
auto file = ofstream(fname);
|
||
|
file << "P7\n";
|
||
|
file << "WIDTH " << input.width << '\n';
|
||
|
file << "HEIGHT " << input.height << '\n';
|
||
|
file << "DEPTH 3\nMAXVAL 255\nTUPLTYPE RGB\nENDHDR\n";
|
||
|
|
||
|
unsigned maxdist = 0;
|
||
|
for (unsigned y = 0; y < input.height; y++) {
|
||
|
for (unsigned x = 0; x < input.width; x++) {
|
||
|
unsigned d = dist[y][x];
|
||
|
if (d > maxdist) maxdist = d;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (unsigned y = 0; y < input.height; y++) {
|
||
|
for (unsigned x = 0; x < input.width; x++) {
|
||
|
uint32_t color = 0;
|
||
|
color |= ((dist[y][x] * 0xFF / maxdist) & 0xFF) << 8;
|
||
|
color |= input.map[y][x] * 0xFF / 26;
|
||
|
if (scan_map[y][x]) color = 0xFF0000;
|
||
|
file.write((char *)&color, 3);
|
||
|
}
|
||
|
}
|
||
|
file.close();
|
||
|
delete scan_map;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
vector<vector<unsigned>> calc_dist(const Input &input)
|
||
|
{
|
||
|
vector<vector<unsigned>> dist(input.height, vector<unsigned>(input.width));
|
||
|
auto &map = input.map;
|
||
|
|
||
|
deque<complex<int>> scan;
|
||
|
scan.push_back(input.end);
|
||
|
dist[input.end.imag()][input.end.real()] = 1;
|
||
|
|
||
|
while (!scan.empty()) {
|
||
|
auto pos = scan.front();
|
||
|
unsigned pos_d = dist[pos.imag()][pos.real()];
|
||
|
unsigned pos_h = map[pos.imag()][pos.real()];
|
||
|
scan.pop_front();
|
||
|
|
||
|
for (auto dir : dirs) {
|
||
|
auto newpos = pos + dir;
|
||
|
|
||
|
// Make sure the new position is in-bounds
|
||
|
if (newpos.imag() < 0 || newpos.imag() >= (int)input.height) continue;
|
||
|
if (newpos.real() < 0 || newpos.real() >= (int)input.width) continue;
|
||
|
|
||
|
// If the distance for this position has already been calculated,
|
||
|
// and is smaller than the current pos' distance, skip it.
|
||
|
unsigned &newpos_d = dist[newpos.imag()][newpos.real()];
|
||
|
if (newpos_d && newpos_d <= pos_d + 1) continue;
|
||
|
|
||
|
// Make sure the height difference isn't too big
|
||
|
unsigned newpos_h = map[newpos.imag()][newpos.real()];
|
||
|
if ((int)pos_h - (int)newpos_h > 1) continue;
|
||
|
|
||
|
// Otherwise, continue down this path
|
||
|
newpos_d = pos_d + 1;
|
||
|
scan.push_back(newpos);
|
||
|
}
|
||
|
#ifdef ANIMATE
|
||
|
render(input, dist, scan);
|
||
|
#endif
|
||
|
}
|
||
|
return dist;
|
||
|
}
|
||
|
|
||
|
unsigned p1(const Input &input, const vector<vector<unsigned>> &dist)
|
||
|
{
|
||
|
unsigned end_dist = dist[input.start.imag()][input.start.real()];
|
||
|
if (end_dist) end_dist--;
|
||
|
return end_dist;
|
||
|
}
|
||
|
|
||
|
unsigned p2(const Input &input, const vector<vector<unsigned>> &dist)
|
||
|
{
|
||
|
unsigned smallest = dist[input.start.imag()][input.start.real()];
|
||
|
for (unsigned y = 0; y < input.height; y++) {
|
||
|
for (unsigned x = 0; x < input.width; x++) {
|
||
|
if (input.map[y][x] == 0) {
|
||
|
unsigned d = dist[y][x];
|
||
|
if (d && d < smallest) smallest = d;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (smallest) smallest--;
|
||
|
return smallest;
|
||
|
}
|
||
|
|
||
|
int main()
|
||
|
{
|
||
|
auto input = parse();
|
||
|
auto dist = calc_dist(input);
|
||
|
cout << p1(input, dist) << endl;
|
||
|
cout << p2(input, dist) << endl;
|
||
|
}
|