Related
I am working on a project that involves generating meshes of triangles from potentially concave polygons. I am far more interested in a performant solution than an optimal solution. Most of what I've come across involves using OpenGL (not an option) or focuses on optimally or approximately optimally decomposing into convex polygons, with O(n3) or O(n4). Ear clipping seems like the most straightforward solution and is supposed to be O(n2):
for /* ever */ {
for each(vertex) {
candidate := Triangle{previousVertex, currentVertex, nextVertex}
if !candidate.IsInterior() || candidate.ContainsAnyOtherVertex() {
continue
}
result.Push(candidate)
vertex = vertex.Except(currentVertex)
}
if len(vertex) == 3 {
result.Push(Triangle(vertex))
break
}
}
I don't see how this can be O(n2). As I see it, this will have to involve three nested loops, each proportional to N: the two explicit loops above, and candidate.ContainsAnyOtherVertex(). Am I missing something? Is there some way to detect whether the candidate triangle contains any of the other remaining vertices that does not involve iterating over all the remaining vertices?
Alternatively, is there a different algorithm I should use? I'd be fine with one that decomposes into convex polygons, but A) I don't care if the solution is optimal, and B) I am not interested in reading through CGAL's source code or academic literature.
Ear clipping is O(n2) if you do it this way:
First, for each vertex with an angle < 180 degrees, count the number of vertexes inside its angle. (O(n2))
You can clip + remove any such vertex with 0 count. You will do this O(n) times. When you remove a vetex:
First remove it from the counts of any triangles that it's in (O(n)); and
Count the other vertexes in the at-most-2 new triangles you form by clipping (also O(n)).
For anyone else having trouble with this, here is a condensed version of what I'm using:
package mesh
import "sort"
type Vec []float32
type Face []Vec
type FaceTri struct {
face Face
index [3]int
}
func triangulate(face Face, offset int) [][3]int {
normal := face.Normal()
index := make([]int, len(face))
convex := map[int]bool{}
reflex := map[int]bool{}
ear := map[int]bool{}
// Mark convex and reflex
for i := range index {
index[i] = i
tri := face.tri(i, i+1, i+2)
// Skip colinear vertices
if tri.Area().Len() == 0 {
continue
}
if tri.Area().Dot(normal) > 0 {
convex[tri.index[1]] = true
} else {
reflex[tri.index[1]] = true
}
}
// Mark ears
for i := range convex {
if isEar(face.tri(i-1, i, i+1), reflex) {
ear[i] = true
}
}
var tris [][3]int
for len(reflex) > 0 || len(index) > 3 {
ni := len(index)
// Next ear
var j int
for j = range ear {
break
}
// Find J in the list of unclipped vertices and get 2 neighbors to each side
x := sort.SearchInts(index, j)
h, i, k, l := (ni+x-2)%ni, (ni+x-1)%ni, (x+1)%ni, (x+2)%ni
h, i, k, l = index[h], index[i], index[k], index[l]
// Clip (I,J,K)
index = append(index[:x], index[x+1:]...)
tris = append(tris, [3]int{offset + i, offset + j, offset + k})
delete(ear, j)
delete(convex, j)
// Update prior vertex
update(face.tri(h, i, k), normal, reflex, convex, ear)
// Update later vertex
if h != l {
update(face.tri(i, k, l), normal, reflex, convex, ear)
}
}
tris = append(tris, [3]int{
offset + index[0],
offset + index[1],
offset + index[2],
})
return tris
}
func update(tri *FaceTri, faceNormal Vec, reflex, convex, ear map[int]bool) {
idx := tri.index[1]
wasConvex, wasEar := convex[idx], ear[idx]
isConvex := wasConvex || tri.Area().Dot(faceNormal) > 0
if !wasConvex && isConvex {
convex[idx] = true
delete(reflex, idx)
}
if !wasEar && isConvex && isEar(tri, reflex) {
ear[idx] = true
}
}
func isEar(tri *FaceTri, reflex map[int]bool) bool {
// It is sufficient to only check reflex vertices - a convex vertex
// cannot be contained without a reflex vertex also being contained
for j := range reflex {
if tri.HasIndex(j) {
continue
}
if tri.ContainsPoint(tri.face[j]) {
return false
}
}
return true
}
// Euclidean length of V
func (v Vec) Len() float32
// Dot product of A and B
func (a Vec) Dot(b Vec) float32
// Calculates the normal vector to the face - for concave polygons, this
// is the summation of the normals of each vertex (normalized)
func (Face) Normal() Vec
// Tri returns a FaceTri for I, J, K, modulo len(f)
func (Face) tri(i, j, k int) *FaceTri
// Area returns the cross product of the vector from v1 to v0 and the vector
// from v1 to v2
func (*FaceTri) Area() Vec
// Returns true if v is in f.index
func (f *FaceTri) HasIndex(v int) bool
// Returns true if P is contained within the described triangle
func (*FaceTri) ContainsPoint(p Vec) bool
This is based on the description from https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf. I think it is essentially the same as what Matt described.
For the sake of clarity and brevity, I omitted the contents of some methods, and cases where I reused results in a way that reduces readability. IMO the only interesting part I left out is ContainsPoint. In 2D, figuring out if triangle ABC contains point P is straight forward. In 3D, less so, since P is not necessarily co-planar with ABC:
V = vector from B to A
U = vector from B to C
Q = vector from B to P
M = MatrixFromColumns(V, U, V x U)
X = (inverse of M) * Q
Q === (V * X[0]) + (U * X[1]) + ((V x U) * X[2])
If X[2] is zero, then Q has a component out of the plane of the triangle
P is in ABC if and only if X[0] and X[1] are positive and X[0] + X[1] <= 1
Problem link: UVa 539 - The Settlers of Catan
(UVa website occasionally becomes down. Alternatively, you can read the problem statement pdf here: UVa External 539 - The Settlers of Catan)
This problem gives a small general graph and asks to find the longest road. The longest road is defined as the longest path within the network that doesn’t use an edge twice. Nodes may be visited more than once, though.
Input Constraints:
1. Number of nodes: n (2 <= n <= 25)
2. Number of edges m (1 <= m <= 25)
3. Edges are un-directed.
4. Nodes have degrees of three or less.
5. The network is not necessarily connected.
Input is given in the format:
15 16
0 2
1 2
2 3
3 4
3 5
4 6
5 7
6 8
7 8
7 9
8 10
9 11
10 12
11 12
10 13
12 14
The first two lines gives the number of nodes n and the number of edges m for this test case respectively. The next m lines describe the m edges. Each edge is given by the numbers of the two nodes connected by it. Nodes are numbered from 0 to n - 1.
The above test can be visualized by the following picture:
Now I know that finding the longest path in a general graph is NP-hard. But as the number of nodes and edges in this problem is small and there's a degree bound of each node, a brute force solution (recursive backtracking) will be able to find the longest path in the given time limit (3.0 seconds).
My strategy to solve the problem was the following:
1. Run DFS (Depth First Search) from each node as the graph can be disconnected
2. When a node visits its neighbor, and that neighbor visits its neighbor and so on, mark the edges as used so that no edge can be used twice in the process
3. When the DFS routine starts to come back to the node from where it began, mark the edges as unused in the unrolling process
4. In each step, update the longest path length
My implementation in C++:
#include <iostream>
#include <vector>
// this function adds an edge to adjacency matrix
// we use this function to build the graph
void addEdgeToGraph(std::vector<std::vector<int>> &graph, int a, int b){
graph[a].emplace_back(b);
graph[b].emplace_back(a); // undirected graph
}
// returns true if the edge between a and b has already been used
bool isEdgeUsed(int a, int b, const std::vector<std::vector<char>> &edges){
return edges[a][b] == '1' || edges[b][a] == '1'; // undirected graph, (a,b) and (b,a) are both valid edges
}
// this function incrementally marks edges when "dfs" routine is called recursively
void markEdgeAsUsed(int a, int b, std::vector<std::vector<char>> &edges){
edges[a][b] = '1';
edges[b][a] = '1'; // order doesn't matter, the edge can be taken in any order [(a,b) or (b,a)]
}
// this function removes edge when a node has processed all its neighbors
// this lets us to reuse this edge in the future to find newer (and perhaps longer) paths
void unmarkEdge(int a, int b, std::vector<std::vector<char>> &edges){
edges[a][b] = '0';
edges[b][a] = '0';
}
int dfs(const std::vector<std::vector<int>> &graph, std::vector<std::vector<char>> &edges, int current_node, int current_length = 0){
int pathLength = -1;
for(int i = 0 ; i < graph[current_node].size() ; ++i){
int neighbor = graph[current_node][i];
if(!isEdgeUsed(current_node, neighbor, edges)){
markEdgeAsUsed(current_node, neighbor, edges);
int ret = dfs(graph, edges, neighbor, current_length + 1);
pathLength = std::max(pathLength, ret);
unmarkEdge(current_node, neighbor, edges);
}
}
return std::max(pathLength, current_length);
}
int dfsFull(const std::vector<std::vector<int>> &graph){
int longest_path = -1;
for(int node = 0 ; node < graph.size() ; ++node){
std::vector<std::vector<char>> edges(graph.size(), std::vector<char>(graph.size(), '0'));
int pathLength = dfs(graph, edges, node);
longest_path = std::max(longest_path, pathLength);
}
return longest_path;
}
int main(int argc, char const *argv[])
{
int n,m;
while(std::cin >> n >> m){
if(!n && !m) break;
std::vector<std::vector<int>> graph(n);
for(int i = 0 ; i < m ; ++i){
int a,b;
std::cin >> a >> b;
addEdgeToGraph(graph, a, b);
}
std::cout << dfsFull(graph) << '\n';
}
return 0;
}
I was ordering what is the worst case for this problem? (I'm wondering it should be n = 25 and m = 25) and in the worst case in total how many times the edges will be traversed? For example for the following test case with 3 nodes and 2 edges:
3 2
0 1
1 2
The dfs routine will be called 3 times, and each time 2 edges will be visited. So in total the edges will be visited 2 x 3 = 6 times. Is there any way to find the upper bound of total edge traversal in the worst case?
Is there any easy solution? Or has anybody an example of an implementation?
Thanks, Jonas
Lets call
a, b, c our three points,
C the circumcircle of (a, b, c)
and d an other point.
A fast way to determine if d is in C is to compute this determinant:
| ax-dx, ay-dy, (ax-dx)² + (ay-dy)² |
det = | bx-dx, by-dy, (bx-dx)² + (by-dy)² |
| cx-dx, cy-dy, (cx-dx)² + (cy-dy)² |
if a, b, c are in counter clockwise order then:
if det equal 0 then d is on C
if det > 0 then d is inside C
if det < 0 then d is outside C
here is a javascript function that does just that:
function inCircle (ax, ay, bx, by, cx, cy, dx, dy) {
let ax_ = ax-dx;
let ay_ = ay-dy;
let bx_ = bx-dx;
let by_ = by-dy;
let cx_ = cx-dx;
let cy_ = cy-dy;
return (
(ax_*ax_ + ay_*ay_) * (bx_*cy_-cx_*by_) -
(bx_*bx_ + by_*by_) * (ax_*cy_-cx_*ay_) +
(cx_*cx_ + cy_*cy_) * (ax_*by_-bx_*ay_)
) > 0;
}
You might also need to check if your points are in counter clockwise order:
function ccw (ax, ay, bx, by, cx, cy) {
return (bx - ax)*(cy - ay)-(cx - ax)*(by - ay) > 0;
}
I didn't place the ccw check inside the inCircle function because you shouldn't check it every time.
This process doesn't take any divisions or square root operation.
You can see the code in action there: https://titouant.github.io/testTriangle/
And the source there: https://github.com/TitouanT/testTriangle
(In case you are interested in a non-obvious/"crazy" kind of solution.)
One equivalent property of Delaunay triangulation is as follows: if you build a circumcircle of any triangle in the triangulation, it is guaranteed not to contain any other vertices of the triangulation.
Another equivalent property of Delaunay triangulation is: it maximizes the minimal triangle angle (i.e. maximizes it among all triangulations on the same set of points).
This suggests an algorithm for your test:
Consider triangle ABC built on the original 3 points.
If the test point P lies inside the triangle it is definitely inside the circle
If the test point P belongs to one of the "corner" regions (see the shaded regions in the picture below), it is definitely outside the circle
Otherwise (let's say P lies in region 1) consider two triangulations of quadrilateral ABCP: the original one contains the original triangle (red diagonal), and the alternate one with "flipped" diagonal (blue diagonal)
Determine which one if this triangulations is a Delaunay triangulation by testing the "flip" condition, e.g. by comparing α = min(∠1,∠4,∠5,∠8) vs. β = min(∠2,∠3,∠6,∠7).
If the original triangulation is a Delaunay triangulation (α > β), P lies outside the circle. If the alternate triangulation is a Delaunay triangulation (α < β), P lies inside the circle.
Done.
(Revisiting this answer after a while.)
This solution might not be as "crazy" as it might appear at the first sight. Note that in order to compare angles at steps 5 and 6 there's no need to calculate the actual angle values. It is sufficient to know their cosines (i.e. there's no need to involve trigonometric functions).
A C++ version of the code
#include <cmath>
#include <array>
#include <algorithm>
struct pnt_t
{
int x, y;
pnt_t ccw90() const
{ return { -y, x }; }
double length() const
{ return std::hypot(x, y); }
pnt_t &operator -=(const pnt_t &rhs)
{
x -= rhs.x;
y -= rhs.y;
return *this;
}
friend pnt_t operator -(const pnt_t &lhs, const pnt_t &rhs)
{ return pnt_t(lhs) -= rhs; }
friend int operator *(const pnt_t &lhs, const pnt_t &rhs)
{ return lhs.x * rhs.x + lhs.y * rhs.y; }
};
int side(const pnt_t &a, const pnt_t &b, const pnt_t &p)
{
int cp = (b - a).ccw90() * (p - a);
return (cp > 0) - (cp < 0);
}
void make_ccw(std::array<pnt_t, 3> &t)
{
if (side(t[0], t[1], t[2]) < 0)
std::swap(t[0], t[1]);
}
double ncos(pnt_t a, const pnt_t &o, pnt_t b)
{
a -= o;
b -= o;
return -(a * b) / (a.length() * b.length());
}
bool inside_circle(std::array<pnt_t, 3> t, const pnt_t &p)
{
make_ccw(t);
std::array<int, 3> s =
{ side(t[0], t[1], p), side(t[1], t[2], p), side(t[2], t[0], p) };
unsigned outside = std::count(std::begin(s), std::end(s), -1);
if (outside != 1)
return outside == 0;
while (s[0] >= 0)
{
std::rotate(std::begin(t), std::begin(t) + 1, std::end(t));
std::rotate(std::begin(s), std::begin(s) + 1, std::end(s));
}
double
min_org = std::min({
ncos(t[0], t[1], t[2]), ncos(t[2], t[0], t[1]),
ncos(t[1], t[0], p), ncos(p, t[1], t[0]) }),
min_alt = std::min({
ncos(t[1], t[2], p), ncos(p, t[2], t[0]),
ncos(t[0], p, t[2]), ncos(t[2], p, t[1]) });
return min_org <= min_alt;
}
and a couple of tests with arbitrarily chosen triangles and a large number of random points
Of course, the whole thing can be easily reformulated without even mentioning Delaunay triangulations. Starting from step 4 this solution is based in the property of the opposite angles of cyclic quadrilateral, which must sum to 180°.
In this Math SE post of mine I included an equation which checks if four points are cocircular by computing a 4×4 determinant. By turning that equation into an inequality you can check for insideness.
If you want to know which direction the inequality has to go, conisder the case of a point very far away. In this case, the x²+y² term will dominate all other terms. So you can simply assume that for the point in question, this term is one while the three others are zero. Then pick the sign of your inequality so this value does not satisfy it, since this point is definitely outside but you want to characterize inside.
If numeric precision is an issue, this page by Prof. Shewchuk describes how to obtain consistent predicates for points expressed using regular double precision floating point numbers.
Given 3 points (x1,y1),(x2,y2),(x3,y3) and the point you want to check is inside the circle defined by the above 3 points (x,y) you can do something like
/**
*
* #param x coordinate of point want to check if inside
* #param y coordinate of point want to check if inside
* #param cx center x
* #param cy center y
* #param r radius of circle
* #return whether (x,y) is inside circle
*/
static boolean g(double x,double y,double cx,double cy,double r){
return Math.sqrt((x-cx)*(x-cx)+(y-cy)*(y-cy))<r;
}
// check if (x,y) is inside circle defined by (x1,y1),(x2,y2),(x3,y3)
static boolean isInside(double x,double y,double x1,double y1,double x2,double y2,double x3,double y3){
double m1 = (x1-x2)/(y2-y1);
double m2 = (x1-x3)/(y3-y1);
double b1 = ((y1+y2)/2) - m1*(x1+x2)/2;
double b2 = ((y1+y3)/2) - m2*(x1+x3)/2;
double xx = (b2-b1)/(m1-m2);
double yy = m1*xx + b1;
return g(x,y,xx,yy,Math.sqrt((xx-x1)*(xx-x1)+(yy-y1)*(yy-y1)));
}
public static void main(String[] args) {
// if (0,1) is inside the circle defined by (0,0),(0,2),(1,1)
System.out.println(isInside(0,1,0,0,0,2,1,1));
}
The method for getting an expression for the center of circle from 3 points goes from finding the intersection of the 2 perpendicular bisectors of 2 line segments, above I chose (x1,y1)-(x2,y2) and (x1,y1)-(x3,y3). Since you know a point on each perpendicular bisector, namely (x1+x2)/2 and (x1+x3)/2, and since you also know the slope of each perpendicular bisector, namely (x1-x2)/(y2-y1) and (x1-x3)/(y3-y1) from the above 2 line segments respectively, you can solve for the (x,y) where they intersect.
A classic algorithm question in 2D version is typically described as
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.
For example, Given the input
[0,1,0,2,1,0,1,3,2,1,2,1]
the return value would be
6
The algorithm that I used to solve the above 2D problem is
int trapWaterVolume2D(vector<int> A) {
int n = A.size();
vector<int> leftmost(n, 0), rightmost(n, 0);
//left exclusive scan, O(n), the highest bar to the left each point
int leftMaxSoFar = 0;
for (int i = 0; i < n; i++){
leftmost[i] = leftMaxSoFar;
if (A[i] > leftMaxSoFar) leftMaxSoFar = A[i];
}
//right exclusive scan, O(n), the highest bar to the right each point
int rightMaxSoFar = 0;
for (int i = n - 1; i >= 0; i--){
rightmost[i] = rightMaxSoFar;
if (A[i] > rightMaxSoFar) rightMaxSoFar = A[i];
}
// Summation, O(n)
int vol = 0;
for (int i = 0; i < n; i++){
vol += max(0, min(leftmost[i], rightmost[i]) - A[i]);
}
return vol;
}
My Question is how to make the above algorithm extensible to the 3D version of the problem, to compute the maximum of water trapped in real-world 3D terrain. i.e. To implement
int trapWaterVolume3D(vector<vector<int> > A);
Sample graph:
We know the elevation at each (x, y) point and the goal is to compute the maximum volume of water that can be trapped in the shape. Any thoughts and references are welcome.
For each point on the terrain consider all paths from that point to the border of the terrain. The level of water would be the minimum of the maximum heights of the points of those paths. To find it we need to perform a slightly modified Dijkstra's algorithm, filling the water level matrix starting from the border.
For every point on the border set the water level to the point height
For every point not on the border set the water level to infinity
Put every point on the border into the set of active points
While the set of active points is not empty:
Select the active point P with minimum level
Remove P from the set of active points
For every point Q adjacent to P:
Level(Q) = max(Height(Q), min(Level(Q), Level(P)))
If Level(Q) was changed:
Add Q to the set of active points
user3290797's "slightly modified Dijkstra algorithm" is closer to Prim's algorithm than Dijkstra's. In minimum spanning tree terms, we prepare a graph with one vertex per tile, one vertex for the outside, and edges with weights equal to the maximum height of their two adjoining tiles (the outside has height "minus infinity").
Given a path in this graph to the outside vertex, the maximum weight of an edge in the path is the height that the water has to reach in order to escape along that path. The relevant property of a minimum spanning tree is that, for every pair of vertices, the maximum weight of an edge in the path in the spanning tree is the minimum possible among all paths between those vertices. The minimum spanning tree thus describes the most economical escape paths for water, and the water heights can be extracted in linear time with one traversal.
As a bonus, since the graph is planar, there's a linear-time algorithm for computing the minimum spanning tree, consisting of alternating Boruvka passes and simplifications. This improves on the O(n log n) running time of Prim.
This problem can be solved using the Priority-Flood algorithm. It's been discovered and published a number of times over the past few decades (and again by other people answering this question), though the specific variant you're looking for is not, to my knowledge, in the literature.
You can find a review paper of the algorithm and its variants here. Since that paper was published an even faster variant has been discovered (link), as well as methods to perform this calculation on datasets of trillions of cells (link). A method for selectively breaching low/narrow divides is discussed here. Contact me if you'd like copies of any of these papers.
I have a repository here with many of the above variants; additional implementations can be found here.
A simple script to calculate volume using the RichDEM library is as follows:
#include "richdem/common/version.hpp"
#include "richdem/common/router.hpp"
#include "richdem/depressions/Lindsay2016.hpp"
#include "richdem/common/Array2D.hpp"
/**
#brief Calculates the volume of depressions in a DEM
#author Richard Barnes (rbarnes#umn.edu)
Priority-Flood starts on the edges of the DEM and then works its way inwards
using a priority queue to determine the lowest cell which has a path to the
edge. The neighbours of this cell are added to the priority queue if they
are higher. If they are lower, then they are members of a depression and the
elevation of the flooding minus the elevation of the DEM times the cell area
is the flooded volume of the cell. The cell is flooded, total volume
tracked, and the neighbors are then added to a "depressions" queue which is
used to flood depressions. Cells which are higher than a depression being
filled are added to the priority queue. In this way, depressions are filled
without incurring the expense of the priority queue.
#param[in,out] &elevations A grid of cell elevations
#pre
1. **elevations** contains the elevations of every cell or a value _NoData_
for cells not part of the DEM. Note that the _NoData_ value is assumed to
be a negative number less than any actual data value.
#return
Returns the total volume of the flooded depressions.
#correctness
The correctness of this command is determined by inspection. (TODO)
*/
template <class elev_t>
double improved_priority_flood_volume(const Array2D<elev_t> &elevations){
GridCellZ_pq<elev_t> open;
std::queue<GridCellZ<elev_t> > pit;
uint64_t processed_cells = 0;
uint64_t pitc = 0;
ProgressBar progress;
std::cerr<<"\nPriority-Flood (Improved) Volume"<<std::endl;
std::cerr<<"\nC Barnes, R., Lehman, C., Mulla, D., 2014. Priority-flood: An optimal depression-filling and watershed-labeling algorithm for digital elevation models. Computers & Geosciences 62, 117–127. doi:10.1016/j.cageo.2013.04.024"<<std::endl;
std::cerr<<"p Setting up boolean flood array matrix..."<<std::endl;
//Used to keep track of which cells have already been considered
Array2D<int8_t> closed(elevations.width(),elevations.height(),false);
std::cerr<<"The priority queue will require approximately "
<<(elevations.width()*2+elevations.height()*2)*((long)sizeof(GridCellZ<elev_t>))/1024/1024
<<"MB of RAM."
<<std::endl;
std::cerr<<"p Adding cells to the priority queue..."<<std::endl;
//Add all cells on the edge of the DEM to the priority queue
for(int x=0;x<elevations.width();x++){
open.emplace(x,0,elevations(x,0) );
open.emplace(x,elevations.height()-1,elevations(x,elevations.height()-1) );
closed(x,0)=true;
closed(x,elevations.height()-1)=true;
}
for(int y=1;y<elevations.height()-1;y++){
open.emplace(0,y,elevations(0,y) );
open.emplace(elevations.width()-1,y,elevations(elevations.width()-1,y) );
closed(0,y)=true;
closed(elevations.width()-1,y)=true;
}
double volume = 0;
std::cerr<<"p Performing the improved Priority-Flood..."<<std::endl;
progress.start( elevations.size() );
while(open.size()>0 || pit.size()>0){
GridCellZ<elev_t> c;
if(pit.size()>0){
c=pit.front();
pit.pop();
} else {
c=open.top();
open.pop();
}
processed_cells++;
for(int n=1;n<=8;n++){
int nx=c.x+dx[n];
int ny=c.y+dy[n];
if(!elevations.inGrid(nx,ny)) continue;
if(closed(nx,ny))
continue;
closed(nx,ny)=true;
if(elevations(nx,ny)<=c.z){
if(elevations(nx,ny)<c.z){
++pitc;
volume += (c.z-elevations(nx,ny))*std::abs(elevations.getCellArea());
}
pit.emplace(nx,ny,c.z);
} else
open.emplace(nx,ny,elevations(nx,ny));
}
progress.update(processed_cells);
}
std::cerr<<"t Succeeded in "<<std::fixed<<std::setprecision(1)<<progress.stop()<<" s"<<std::endl;
std::cerr<<"m Cells processed = "<<processed_cells<<std::endl;
std::cerr<<"m Cells in pits = " <<pitc <<std::endl;
return volume;
}
template<class T>
int PerformAlgorithm(std::string analysis, Array2D<T> elevations){
elevations.loadData();
std::cout<<"Volume: "<<improved_priority_flood_volume(elevations)<<std::endl;
return 0;
}
int main(int argc, char **argv){
std::string analysis = PrintRichdemHeader(argc,argv);
if(argc!=2){
std::cerr<<argv[0]<<" <Input>"<<std::endl;
return -1;
}
return PerformAlgorithm(argv[1],analysis);
}
It should be straight-forward to adapt this to whatever 2d array format you are using
In pseudocode, the following is equivalent to the foregoing:
Let PQ be a priority-queue which always pops the cell of lowest elevation
Let Closed be a boolean array initially set to False
Let Volume = 0
Add all the border cells to PQ.
For each border cell, set the cell's entry in Closed to True.
While PQ is not empty:
Select the top cell from PQ, call it C.
Pop the top cell from PQ.
For each neighbor N of C:
If Closed(N):
Continue
If Elevation(N)<Elevation(C):
Volume += (Elevation(C)-Elevation(N))*Area
Add N to PQ, but with Elevation(C)
Else:
Add N to PQ with Elevation(N)
Set Closed(N)=True
This problem is very close to the construction of the morphological watershed of a grayscale image.
One approach is as follows (flooding process):
sort all pixels by increasing elevation.
work incrementally, by increasing elevations, assigning labels to the pixels per catchment basin.
for a new elevation level, you need to label a new set of pixels:
Some have no labeled
neighbor, they form a local minimum configuration and begin a new catchment basin.
Some have only neighbors with the same label, they can be labeled similarly (they extend a catchment basin).
Some have neighbors with different labels. They do not belong to a specific catchment basin and they define the watershed lines.
You will need to enhance the standard watershed algorithm to be able to compute the volume of water. You can do that by determining the maximum water level in each basin and deduce the ground height on every pixel. The water level in a basin is given by the elevation of the lowest watershed pixel around it.
You can act every time you discover a watershed pixel: if a neighboring basin has not been assigned a level yet, that basin can stand the current level without leaking.
In order to accomplish tapping water problem in 3D i.e., to calculate the maximum volume of trapped rain water you can do something like this:
#include<bits/stdc++.h>
using namespace std;
#define MAX 10
int new2d[MAX][MAX];
int dp[MAX][MAX],visited[MAX][MAX];
int dx[] = {1,0,-1,0};
int dy[] = {0,-1,0,1};
int boundedBy(int i,int j,int k,int in11,int in22)
{
if(i<0 || j<0 || i>=in11 || j>=in22)
return 0;
if(new2d[i][j]>k)
return new2d[i][j];
if(visited[i][j]) return INT_MAX;
visited[i][j] = 1;
int r = INT_MAX;
for(int dir = 0 ; dir<4 ; dir++)
{
int nx = i + dx[dir];
int ny = j + dy[dir];
r = min(r,boundedBy(nx,ny,k,in11,in22));
}
return r;
}
void mark(int i,int j,int k,int in1,int in2)
{
if(i<0 || j<0 || i>=in1 || j>=in2)
return;
if(new2d[i][j]>=k)
return;
if(visited[i][j]) return ;
visited[i][j] = 1;
for(int dir = 0;dir<4;dir++)
{
int nx = i + dx[dir];
int ny = j + dy[dir];
mark(nx,ny,k,in1,in2);
}
dp[i][j] = max(dp[i][j],k);
}
struct node
{
int i,j,key;
node(int x,int y,int k)
{
i = x;
j = y;
key = k;
}
};
bool compare(node a,node b)
{
return a.key>b.key;
}
vector<node> store;
int getData(int input1, int input2, int input3[])
{
int row=input1;
int col=input2;
int temp=0;
int count=0;
for(int i=0;i<row;i++)
{
for(int j=0;j<col;j++)
{
if(count==(col*row))
break;
new2d[i][j]=input3[count];
count++;
}
}
store.clear();
for(int i = 0;i<input1;i++)
{
for(int j = 0;j<input2;j++)
{
store.push_back(node(i,j,new2d[i][j]));
}
}
memset(dp,0,sizeof(dp));
sort(store.begin(),store.end(),compare);
for(int i = 0;i<store.size();i++)
{
memset(visited,0,sizeof(visited));
int aux = boundedBy(store[i].i,store[i].j,store[i].key,input1,input2);
if(aux>store[i].key)
{
memset(visited,0,sizeof(visited));
mark(store[i].i,store[i].j,aux,input1,input2);
}
}
long long result =0 ;
for(int i = 0;i<input1;i++)
{
for(int j = 0;j<input2;j++)
{
result = result + max(0,dp[i][j]-new2d[i][j]);
}
}
return result;
}
int main()
{
cin.sync_with_stdio(false);
cout.sync_with_stdio(false);
int n,m;
cin>>n>>m;
int inp3[n*m];
store.clear();
for(int j = 0;j<n*m;j++)
{
cin>>inp3[j];
}
int k = getData(n,m,inp3);
cout<<k;
return 0;
}
class Solution(object):
def trapRainWater(self, heightMap):
"""
:type heightMap: List[List[int]]
:rtype: int
"""
m = len(heightMap)
if m == 0:
return 0
n = len(heightMap[0])
if n == 0:
return 0
visited = [[False for i in range(n)] for j in range(m)]
from Queue import PriorityQueue
q = PriorityQueue()
for i in range(m):
visited[i][0] = True
q.put([heightMap[i][0],i,0])
visited[i][n-1] = True
q.put([heightMap[i][n-1],i,n-1])
for j in range(1, n-1):
visited[0][j] = True
q.put([heightMap[0][j],0,j])
visited[m-1][j] = True
q.put([heightMap[m-1][j],m-1,j])
S = 0
while not q.empty():
cell = q.get()
for (i, j) in [(1,0), (-1,0), (0,1), (0,-1)]:
x = cell[1] + i
y = cell[2] + j
if x in range(m) and y in range(n) and not visited[x][y]:
S += max(0, cell[0] - heightMap[x][y]) # how much water at the cell
q.put([max(heightMap[x][y],cell[0]),x,y])
visited[x][y] = True
return S
Here is the simple code for the same-
#include<iostream>
using namespace std;
int main()
{
int n,count=0,a[100];
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
}
for(int i=1;i<n-1;i++)
{
///computing left most largest and Right most largest element of array;
int leftmax=0;
int rightmax=0;
///left most largest
for(int j=i-1;j>=1;j--)
{
if(a[j]>leftmax)
{
leftmax=a[j];
}
}
///rightmost largest
for(int k=i+1;k<=n-1;k++)
{
if(a[k]>rightmax)
{
rightmax=a[k];
}
}
///computing hight of the water contained-
int x=(min(rightmax,leftmax)-a[i]);
if(x>0)
{
count=count+x;
}
}
cout<<count;
return 0;
}
how to find the number of nodes in a loop of linked list?
for e.g
A ----> B ----> C -----> D -----> E
Λ |
| |
| V
H <----- G <----- F
Find the number of nodes in a loop from C to H
Fundamental problem is how to find point C. We can use traditional hare and tortoise algo but it does not meet every time at point C.
See here more solutions for how to find a loop in a linked list. Adding the nodes counting is pretty simple then. (Although The Tortoise and the Hare is probably the best one)
If you simply want to find the answer, do the tortoise-hare to determine at what point there is definitely a loop. Then start a counter, and count how many iterations you must make to reach the point that you first found. This may not be the most efficient possible, but it gives a correct answer.
Some C++ code:
#include <iostream>
struct node
{
node(node* next)
: next(next)
{ }
node* next;
};
int main(int argc, char* argv[])
{
node h(NULL), g(&h), f(&g), e(&f), d(&e), c(&d), b(&c), a(&b);
h.next = &c;
node* tortoise = &a;
node* hare = &b;
while(tortoise != hare)
{
tortoise = tortoise->next;
hare = hare->next->next;
}
int count = 1;
tortoise = tortoise->next;
while(tortoise != hare)
{
++count;
tortoise = tortoise->next;
}
std::cout << "Size of cycle: " << count << "\n";
return 0;
}
Note that you'll have to do some extra work to determine if you hit the end, rather than looping, in the case that you don't actually have a cycle. Traditional tortoise-hare should take care of that:
http://en.wikipedia.org/wiki/Cycle_detection
List visited;
List toVisit;
toVisit.add(A); // add the first Node
while(toVisit is not empty){
Node current = visited.remove();
Array <Node> links = current.getLinks();
for(int i=0; i<links.size(); i++){
if(!visited.contains(links[i])){ // if the link has NOT already been visited add it to the toVisit List
toVisit.add(links[i]);
}
visited.add(current); // mark current as visited
}
}
return visited.size(); // to get the number of nodes in the graph
I don't think that I would consider this a linkedList. LinkedLists usually end with a null pointer or a pointer pointing to an ending symbol. Ie: start -> A -> B -> C -> end. I think that this would be a specific kind of graph.
To find the total number of nodes in the graph I would do this:
List visited;
List toVisit;
toVisit.add(A); // add the first Node
while(toVisit is not empty){
Node current = visited.remove();
Array <Node> links = current.getLinks();
for(int i=0; i<links.size(); i++){
if(!visited.contains(links[i])){ // if the link has NOT already been visited add it to the toVisit List
toVisit.add(links[i]);
}
visited.add(current); // mark current as visited
}
}
return visited.size(); // to get the number of nodes in the graph
If you always know that there will one loop like (note the ...):
A ---> ... ---> C -----> D -----> E
Λ |
| |
| V
... <----- G <--- F
You could modify the code like this:
List visited;
Node current = firstNode;
while(!visited.contains(firstNode)){
Node next = current.getNext();
visited.add(current); // mark current as visited
current=next;
}
// our ending condition is when we have found the same node again.
int currentIndex = visited.indexOf(current);
int size = visited.size();
int sizeOfLoop = size - currentIndex;
return sizeOfLoop;
1) flyod alogo find the loop
2) when slow_ptr=fast_ptr , find the number of nodes in loop (k)
Additionally you can also go to C like this:-
3) start 2 ptr , one from head and another from head+k.
4) You will meet at starting of Loop (C)