Andrej Staruch Andrej Staruch - 2 months ago 13
C++ Question

Trouble with DFS in solving coastline length

I'm trying to solve one problem, which I found on website https://open.kattis.com/problems/coast. Tl;dr version of problem is, that for given map of landscape, I should print out length of coastline (without inner islands).

My idea was, to solve this by adding additional layer and then start DFS, so the algorithm will walk through every possible tile in map, and then watch on every tile, how many borders are around the tile.

However, for specific input, is my algorithm not working. When I've submitted the solution on this site (open.kattis), it says, that my program is giving wrong answer in 9th of 26 tests (previous 8 test were ok), but without any further explanation.

Can somebody look at my program, and say me, why is it bad? Where did I do mistake? Thanks

#include <iostream>
#include <stack>
#include <sstream>

using namespace std;

int main() {
string line;
getline(cin, line);

int rows = 0;
int columns = 0;

stringstream stream(line);
stream >> rows;
stream >> columns;

int map[rows][columns];

for (int i = 0; i < rows; i++) {
getline(cin, line);
for (int j = 0; j < columns; j++) {
map[i][j] = line[j] - 48;
}
}
//parsed landscape into 2d array

// int rows = 5;
// int columns = 6;
// int map[rows][columns] = {
// {0, 1, 1, 1, 1, 0,},
// {0, 1, 0, 1, 1, 0,},
// {1, 1, 1, 0, 0, 0,},
// {0, 0, 0, 0, 1, 0,},
// {0, 0, 0, 0, 0, 0,},
// };

int bigMap[rows+2][columns+2];
bool visited[rows+2][columns+2];

//create bigger map, so DFS can start from corner and assume
//that there is water around everywhere
//also initialize array visited for DFS

//add 2 new rows, before and after existing one
for (int i = 0; i < columns+2; i++) {
bigMap[0][i] = 0;
bigMap[rows + 1][i] = 0;

visited[0][i] = false;
visited[rows + 1][i] = false;
}

//add 2 new columns, before and after existing
//copy original map to new one
for (int i = 0; i < rows; i++) {
bigMap[i+1][0] = 0;
bigMap[i+1][columns + 1] = 0;

visited[i+1][0] = false;
visited[i+1][columns + 1] = false;
for (int j = 0; j < columns; j++) {
bigMap[i+1][j+1] = map[i][j];

visited[i+1][j+1] = false;
}
}
rows += 2;
columns += 2;

//starting DFS
int x = 0, y = 0;
//visited[x][y] = true; <-- edit
pair <int, int> coordinates;

coordinates.first = x;
coordinates.second = y;

stack<pair <int, int> > st;

//first vertex in stack
st.push(coordinates);

//total sum of borders
int borders = 0;

while(!st.empty()) {
//check coordinates in each round
x = st.top().first;
y = st.top().second;

//navigate to new vertex (only if new vertex wasn't visited (visited[x][y] == 0) and only
//if there is water (bigMap[x][y] == 0) and check if new vertex is still in the map
//if there is no possible vertex, then we reached the end so then pop the vertex and
//look in another way
if (visited[x][y+1] == 0 && bigMap[x][y+1] == 0 && y + 1 < columns) {
y++;
coordinates.second = y;
st.push(coordinates);
} else {
if (visited[x+1][y] == 0 && bigMap[x+1][y] == 0 && x + 1 < rows) {
x++;
coordinates.first = x;
st.push(coordinates);
} else {
if (visited[x][y-1] == 0 && bigMap[x][y-1] == 0 && y > 0) {
y--;
coordinates.second = y;
st.push(coordinates);
} else {
if (visited[x-1][y] == 0 && bigMap[x-1][y] == 0 && x > 0) {
x--;
coordinates.first = x;
st.push(coordinates);
} else {
st.pop();
continue;
}
}
}
}
//visited new vertex, so look around him and count borders
visited[x][y] = true;
if (bigMap[x][y+1] == 1 && y + 1 < columns) borders++;
if (bigMap[x+1][y] == 1 && x + 1< rows) borders++;
if (bigMap[x][y-1] == 1 && y > 0) borders++;
if (bigMap[x-1][y] == 1 && x > 0) borders++;
}
cout << borders << endl;

return 0;

Answer

The issue is that you are reusing the variable coordinates each time around the loop without setting it to the correct value. Your if test cascade is assuming that coordinates is set to the current location. This is only true while you are descending in your dfs. Once you start ascending again, the coordinate will be pointing to the wrong place.

Simple solution, add

    coordinates = st.top();

at the top of your loop.

Here is a sample map that it will currently get wrong.

5 6
000000
011100
001010
000100
000000

Answer should be 14, but currently you get 18 as the program reaches the lake at row 3, column 4.

To check that it is doing this, add a debugging line at the end of your loop, where it is adding the borders.

cout << "adding " << x << " " << y << "\n"; 

You can then verify if the program is considering any locations it shouldn't.