How to use MPI Cartesian Topology Correctly - parallel-processing

To start off, I needed to calculate a number of sums and then find the minimum of those sums, this was done as so, using mpi:
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
.
.
.
x = (size)/numprocs;
low = myid * x;
high = low + x;
for(i =low; i < high; i++){
for(j = 0; j < matrixDim; j++){
for(k = 0; k < matrixDim; k+=gap){
for(m = 0; m < matrixDim; m+=gap){
c1 = calculation1(i,j,k,m);
if(c1 > cutoff){
sum += calculation2(modifier1[k][m], modifier2[k][m]);
}
}
}
if(sum < min){
min = sum;
minI = i;
minJ = j;
}
sum = 0;
}
}
MPI_Reduce(&result, &minimum, 1, MPI_FLOAT, MPI_MIN, 0, MPI_COMM_WORLD);
if( 0 == myid)
printf("The min is: %f", minimum);
MPI_Finalize();
However, now instead of finding the minimum sum of the whole 2D matrix, I need to find the minimum sum of every partition in the matrix, a partition will be a square defined by four points, and no matter the matrix size, there will always be 16 squares (the matrix is no smaller than 800 * 800). I'm trying to implement this using a MPI Cartesian topology, however I'm having trouble wrapping my head around the implementation. Any help, or tips would be appreciated.

This is more of an extended comment than an answer ...
Like francesco I'm not sure that I see the need for a cartesian (or any other) topology here. If your problem is as you describe it each MPI process can compute a partition minimum sum without either sending or receiving data from the other processes (apart, probably, from an initial scatter and terminal gather).
Toplogies are generally used for situations where the problem decomposes into pieces and those pieces have some idea of relative neighbourliness: in a cartesian topology a process (or piece of the problem) might have east, west, north and south neighbours for example. I don't see such a concept here, nor any utility in forcing it onto this problem.

Related

algorithm problem: uniform noise binary image classification

I have a very interesting algorithm problem (not image processing!). But I still don't understand. Please help me.
Problem:
There are 10 patterns with 4×4 size (binary). For example,
0001
0011
0111
0001
and there's a 16×16 board (it is initialized to 0).
Now, let's choose one of 10 patterns and put it in a random position on the 16×16 board (position and pattern are selected randomly). For example,
000000000....
000100000....
001100000
001100000
000100000
000000000
000000000
........
After that, each value will be flipped with a 10% probability. For example,
000000000....
000100010....
001000000
001100100
000100000
010000000
000000100
........
Here, the problem is to guess which pattern originally existed(accuracy more than 70% allowed). In other words, out of 100 queries, it has to be successful 70 times or more.
My first approach was to calculate the accuracy for every possible patch and pattern. For example,
int NOISE_IMAGE[16][16];
int PATTERN[10][4][4];
double getScore(int x, int y, int pIdx){
int confusion[2][2] = { 0, };
for (int i = 0; i < 4; i++){
for (int j = 0; j < 4; j++){
confusion[NOISE_IMAGE[x + i][y + j]][PATTERN[pIdx][i][j]]++;
}
}
return (double)(confusion[0][0] + confusion[1][1]) / 16.;
}
void solve(){
for (int pattern = 0; pattern < 10; pattern++){
for (int x = 0; x < 14; x++){
for (int y = 0; y < 14; y++){
double score = getScore(x, y, pattern);
}
}
}
}
However, this approach has disastrous results. I think it's because the more zeros in the pattern, the higher the score.
A successful approach simply computes the difference only in the region where the pattern is 1.
int getScore(int x, int y, int pIdx){
int confusion[2][2] = { 0, };
for (int i = 0; i < 4; i++){
for (int j = 0; j < 4; j++){
confusion[NOISE_IMAGE[x + i][y + j]][PATTERN[pIdx][i][j]]++;
}
}
return confusion[1][1] - confusion[0][1];
}
I don't understand why this formula came up. Why don't we need to consider regions where the pattern is zero?
After more study, I was able to get the following formula:
Let's assume
1 (pattern)
0 (pattern)
1 (noise image)
a
c
0 (noise image)
b
d
Then, given a pattern and a noise image patch (4×4), the probability that a pattern be a noise image patch is as follows.
(9/10)(a+d) * (1/10)(b+c)
In short,
9(a+d)/1016
So, shouldn't it be proportional to a+d? But the answer is proportional to a-b.
My question is, in the above problem, why is the answer proportional to a-d, and why is the correct answer when it is 0 without considering it? please help me..
Because 16x16 board was initialized to 0, unless the number of 1 in the pattern is extremely small, it will be extremely unlikely that "10% flipping" will mislead the location of the pattern.
In other words, "Where the pattern existed" is automatically solved.
Therefore, the question is essentially "I applied 10% flipping to a specific 4x4 pattern. Which is the original pattern?"
I think that, which of the following groups is more effective for this problem will depend on the content of the 10 patterns.
a and b : "1(pattern) must be 1(noise image)"
c and d : "0(pattern) must be 0(noise image)"
If the shapes composed of 1 are characteristic and are not sufficiently similar to each other, the former(a and b) should be evaluated.
In this case, even if some 1 are lost/caused by "flip", it will not affect the shape distinction.
Adding c and d to the evaluation can only increase the likelihood of misidentification caused by "0 to 1 flipping".
(I think your situation is like this.)
If most of the places in the pattern are 1 and only a few of the rest are 0, the story is reversed.

Binary Lifting | Planet Queries 1 | TLE

I am solving this problem on CSES.
Given n planets, with exactly 1 teleporter on each planet which teleports us to some other planet (possibly the same), we have to solve q queries. Each query is associated with a start planet, x and a number of teleporters to traverse, k. For each query, we need to tell where we would reach after going through k teleporters.
I have attempted this problem using the binary lifting concept.
For each planet, I first saved the planets we would reach by going through 20, 21, 22,... teleporters.
Now, as per the constraints (esp. for k) provided in the question, we need only store the values till 231.
Then, for each query, starting from the start planet, I traverse through the teleporters using the data in the above created array (in 1) to mimic the binary expansion of k, the number of teleporters to traverse.
For example, if k = 5, i.e. (101)2, and the initial planet is x, I first go (001)2 = 1 planet ahead, using the array, let's say to planet y, and then (100)2 = 4 planets ahead. The planet now reached is the required result to the query.
Unfortunately, I am receiving TLE (time limit exceeded) error in the last test case (test 12).
Here's my code for reference:
#define inp(x) ll x; scanf("%lld", &x)
void solve()
{
// Inputting the values of n, number of planets and q, number of queries.
inp(n);
inp(q);
// Inputting the location of next planet the teleporter on each planet points to, with correction for 0 - based indexing
vector<int> adj(n);
for(int i = 0; i < n; i++)
{
scanf("%d", &(adj[i]));
adj[i]--;
}
// maxN stores the maximum value till which we need to locate the next reachable plane, based on constraints.
// A value of 32 means that we'll only ever need to go at max 2^31 places away from the planet in query.
int maxN = 32;
// This array consists of the next planet we can reach from any planet.
// Specifically, par[i][j] is the planet we get to, on passing through 2^j teleporters starting from planet i.
vector<vector<int>> par(n, vector<int>(maxN, -1));
for(int i = 0; i < n; i++)
{
par[i][0] = adj[i];
}
for(int i = 1; i < maxN; i++)
{
for(int j = 0; j < n; j++)
{
ll p1 = par[j][i-1];
par[j][i] = par[p1][i-1];
}
}
// This task is done for each query.
for(int i = 0; i < q; i++)
{
// x is the initial planet, corrected for 0 - based indexing.
inp(x);
x--;
// k is the number of teleporters to traverse.
inp(k);
// cur is the planet we currently are at.
int cur = x;
// For every i'th bit in k that is 1, the current planet is moved to the planet we reach to by moving through 2^i teleporters from cur.
for(int i = 0; (1 << i) <= k ; i++)
{
if(k & (1 << i))
{
cur = par[cur][i];
}
}
// Once the full binary expansion of k is used up, we are at cur, so (cur + 1) is the result because of the judge's 1 - based indexing.
cout<<(cur + 1)<<endl;
}
}
The code gives the correct output in every test case, but undergoes TLE in the final one (the result in the final one is correct too, just a TLE occurs). According to my observation the complexity of the code is O(32 * q + n), which doesn't seem to exceed the 106 bound for linear time code in 1 second.
Are there any hidden costs in the algorithm I may have missed, or some possible optimization?
Any help appreciated!
It looks to me like your code works (after fixing the scanf), but your par map could have 6.4M entries in it, and precalculating all of those might just get you over the 1s time limit.
Here are a few things to try, in order of complexity:
replace par with a single vector<int> and index it like par[i*32+j]. This will remove a lot of double indirections.
Buffer the output in a std::string and write it in one step at the end, in case there's some buffer flushing going on that you don't know about. I don't think so, but it's easy to try.
Starting at each planet, you enter a cycle in <= n steps. In O(n) time, you can precalculate the distance to the terminal cycle and the size of the terminal cycle for all planets. Using this information you can reduce each k to at most 20000, and that means you only need j <= 16.

Minimizing the distance of pairing points

My problem is as follows:
Given a number of 2n points, I can calculate the distance between all points
and get a symmetrical matrix.
Can you create n pairs of points, so that the sum of the distance of all pairs is
minimal?
EDIT: Every point has to be in one of the pairs. Which means that
every point is only allowed to be in one pair.
I have naively tried to use the Hungarian algorithm and hoped that it may give me an assignment, so that the assignments are symmetrical. But that obviously did not work, as I do not have a bipartite graph.
After a search, I found the Stable roommates problem, which seems to be similar to my problem, but the difference is, that it just tries to find a matching, but not to try to minimize some kind of distance.
Does anyone know a similar problem or even a solution? Did I miss something? The problem does actually not seem that difficult, but I just could not come up with an optimal solution.
There's a primal-dual algorithm due to Edmonds (the Blossom algorithm), which you really don't want to implement yourself if possible. Vladimir Kolmogorov has an implementation that may be suitable for your purposes.
Try network-flow. The max flow is the number of the pairs you want to create. And calculate the min cost of it.
now this isn't a guarantee but just a hunch.
you can find the shortest pair, match them, and remove it from the set.
and recurse until you have no pairs left.
It is clearly sub-optimal. but I have a hunch that the ratio of just how sub-optimal this is to the absolutely optimal solution can be bounded. The hope is to use some sub-modularity argument and bound it to something like (1 - 1 / e) fraction of the global optimal, but I wasn't able to do it. Maybe someone could take a stab at it.
There is a C++ memoization implementation in Competitive Programming 3 as follows (note maximum of N was 8):
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
int N, target;
double dist[20][20], memo[1<<16];
double matching(int bitmask)
{
if (memo[bitmask] > -0.5) // Already computed? Then return the result if yes
return memo[bitmask];
if (bitmask == target) // If all students are already matched then cost is zero
return memo[bitmask] = 0;
double ans = 2000000000.0; // Infinity could also work
int p1, p2;
for (p1 = 0; p1 < 2*N; ++p1) // Find first non-matched point
if (!(bitmask & (1 << p1)))
break;
for (p2 = p1 + 1; p2 < 2*N; ++p2) // and pair it with another non-matched point
if (!(bitmask & (1 << p2)))
ans = min(ans, dist[p1][p2]+matching(bitmask| (1 << p1) | (1 << p2)));
return memo[bitmask] = ans;
}
and then the main method (driving code)
int main()
{
int i,j, caseNo = 1, x[20], y[20];
while(scanf("%d", &N), N){
for (i = 0; i < 2 * N; ++i)
scanf("%d %d", &x[i], &y[i]);
for (i = 0; i < 2*N - 1; ++i)
for (j = i + 1; j < 2*N; ++j)
dist[i][j] = dist[j][i] = hypot(x[i]-x[j], y[i]-y[j]);
// use DP to solve min weighted perfect matching on small general graph
for (i = 0; i < (1 << 16); ++i) memo[i] = -1;
target = (1 << (2 * N)) - 1;
printf("Case %d: %.2lf", caseNo++, matching(0));
}
return 0;
}

Given k-coloring of graph's vertices calculate (k-1)-coloring

It's a common knowledge that coloring vertices of a graph is NP-complete.
It's also known that there are efficient greedy algorithms that can get an approximate solution.
Why not use these randomized greedy algorithms to calculate a coloring with k colors and then use some slower algorithms to reduce k?
In my situation I know the minimal number of colors that are sufficient to color graph G - let's call it K. I've also managed to implement SL algorithm which gave me (K+2)-coloring. One color was used only to color one vertex so I managed to remove it by manually recoloring some other nodes. Therefore, I have (K+1)-coloring and would like to write an algorithm that would reduce K (or rather K+1) by 1.
I've tried to do it manually - I found a color that is used in the minimal number of vertices colored by the same color and reduced this color's uses to 3. I have to recolor only 3 nodes.
One idea is to make 3 recursive calls - one for each badly colored nodes. Let's analyze what the recursive function would have to do for node v. It would have to check every color apart from v's color and the one we'd like to remove. So for each color c it should set v's color to c and make a recursive call for each node which is a neighbour of v and has color c. After checking all colors we should retrieve v's old color and set it again. One more optimisation may be not trying to change v's color to one that more than x of his neighbours has (as the recursion tree would be too deep) - but for too small x it may not be able to change the color at all.
Another idea is to check nodes whose color can be changed (not to a color that we want to remove) so that it wouldn't collide with neighbours' colors. And make recursive calls to change other nodes' colors until one color which we want to remove will be recolored.
Here's my implementation of the first algorithm which was intended to work for n < 90 but doesn't seem to end (500 minutes of execution):
#include<stdio.h>
#include<assert.h>
#include<vector>
using namespace std;
vector<int> graph[99];
int hash[10009], color[99];
const int colors = 9, color_to_change = 7;
void change_color(int v)
{
int tmp = color[v], count;
for(int i = 1; i <= colors; ++i)
{
count = 0;
for(int j = 0; j < graph[v].size(); ++j)
count += color[graph[v][j]] == i;
if(!count)
{
color[v] = i;
return;
}
if(count < 4 && i != color_to_change && i != color[v])
{
color[v] = i;
for(int j = 0; j < graph[v].size(); ++j)
if(color[graph[v][j]] == i)
change_color(graph[v][j]);
}
}
color[v] = tmp;
}
int main()
{
int n, m, a, b, max = 0, j = -1;
scanf("%d%d", &n, &m);
while(m--)
{
scanf("%d%d", &a, &b);
assert(a != b);
if(hash[a*100+b] || hash[b*100+a])
continue;
assert(a*100+b < 10000 && b*100+a < 10000);
hash[a*100+b] = hash[b*100+a] = 1;
graph[a].push_back(b);
graph[b].push_back(a);
}
for(int i = 1; i <= n; ++i)
scanf("%d", &color[i]);
for(int i = 1; i <= n; ++i)
if(color[i] == color_to_change)
change_color(i);
for(int i = 1; i <= n; ++i)
printf("%d ", color[i]);
return 0;
}
Any ideas how to make it faster?
I've only looked at the code briefly, and read your explaination, but it seems you get into an infinite loop with switching back and forth between neighbours. You'll need to store a flag in each node to note that it's currently being recoloured, and only recurse into those neighbours that are not currently being recoloured.
However - this algorithm looks like its exponential in the worst case - and I'm pretty sure there are cases where a K coloured graph can not be recoloured to a K-1 graph without changing some large fraction of the graphs, even if the number of nodes of colour K is only 1.
Here's an example Graph with simple topology. Its clear that it can be two coloured (R,G), and we have a three colour version using (R,G,B). The only way to recolour it correctly is to change approximately 1/2 the nodes colours, ending up with one of the other versions below. () denotes the single node of colour B, and [] denotes the sections that need to get recoloured.
3 colour version : R-G-R-G-R-G-(B)-R-G-R-G-R-G-R
2 colour version 1: [R-G-R-G-R-G- R]-G-R-G-R-G-R-G
2 colour version 2: G-R-G-R-G-R-[G -R-G-R-G-R-G-R]
This means the minimum depth of your (potentially exponential) search may be more than 1/2 the number of nodes. This may kill sensible performance times (or may not depending on the topology of the graphs I guess.)

The Maximum Volume of Trapped Rain Water in 3D

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;
}

Resources