is there a way to read inputs from txt file
6
0 1 1
2 3 1
1 2 1
3 0 1
4 0 1
4 5 1
3 4 1
5 3 1
for the part
int V = 6;
Graph g(V);
g.insertEdge(0,1,1);
g.insertEdge(2,3,1);
g.insertEdge(1,2,1);
g.insertEdge(3,0,1);
g.insertEdge(4,0,1);
g.insertEdge(4,5,1);
g.insertEdge(3,4,1);
g.insertEdge(5,3,1);
#include<bits/stdc++.h>
using namespace std;
# define INF 0x3f3f3f3f
// creating a struct for an edge
struct Edge
{
int u, v, wt;
};
class Graph
{
int V;
// adjacency list
list < pair <int, int > >*adjacency;
vector < Edge > edges;
public :
Graph( int V )
{
this->V = V ;
adjacency = new list < pair <int, int > >[V];
}
// declaring all the functions
// inserting an edge
void insertEdge ( int u, int v, int w );
// deleting an edge
void deleteEdge( int u, int v, int w );
// finding minimum path
int minimumPath (int u, int v );
// deleting an edge
void deleteEdge( int u, int v );
// finding min cycles
int FindMinCycle();
};
// inserting an edge
void Graph :: insertEdge ( int u, int v, int w )
{
adjacency[u].push_back( make_pair( v, w ));
adjacency[v].push_back( make_pair( u, w ));
Edge e = { u, v, w };
edges.push_back ( e );
}
// deleting an edge
void Graph :: deleteEdge( int u, int v, int w )
{
adjacency[u].remove(make_pair( v, w ));
adjacency[v].remove(make_pair(u, w ));
}
// finding minimum path function
int Graph :: minimumPath ( int u, int v )
{
// storing vertices
set< pair<int, int> > setds;
// vector for distances
vector<int> dist(V, INF);
/* insert self source at first and initialize its distance as 0 */
setds.insert(make_pair(0, u));
dist[u] = 0;
while (!setds.empty())
{
/* The first vertex in Set is the one with the shortest distance; remove it from Set. */
pair<int, int> tmp = *(setds.begin());
setds.erase(setds.begin());
/* To preserve the vertices sorted distance, vertex label must be put in second of pair (distance must be first item in pair) */
int u = tmp.second;
list< pair<int, int> >::iterator i;
for (i = adjacency[u].begin(); i != adjacency[u].end(); ++i)
{
int v = (*i).first;
int weight = (*i).second;
if (dist[v] > dist[u] + weight)
{
/* If the distance of v is not INF, then it must be in our set; therefore, it should be removed and reinserted with an updated shorter distance. We only remove from Set the vertices for which the distance has been determined. Therefore, they would never see us arrive here. */
if (dist[v] != INF)
setds.erase(setds.find(make_pair(dist[v], v)));
dist[v] = dist[u] + weight;
setds.insert(make_pair(dist[v], v));
}
}
}
return dist[v] ;
}
// finding minimum path function
int Graph :: FindMinCycle ( )
{
int min_cycle = INT_MAX;
int E = edges.size();
for ( int i = 0 ; i < E ; i++ )
{
Edge e = edges[i];
/* Obtain the edge vertices that we currently delete from the graph, and then use Dijkstra's shortest path technique to discover the shortest path between these two vertices. */
deleteEdge( e.u, e.v, e.wt ) ;
int dist = minimumPath( e.u, e.v );
/* If this is the shortest cycle, update min cycle; otherwise, add weight to currently deleted edges to create a cycle. */
min_cycle = min(min_cycle, dist + e.wt);
// add current edge back to the graph
insertEdge( e.u, e.v, e.wt );
}
return min_cycle ;
}
int main()
{
int V = 6;
Graph g(V);
g.insertEdge(0,1,1);
g.insertEdge(2,3,1);
g.insertEdge(1,2,1);
g.insertEdge(3,0,1);
g.insertEdge(4,0,1);
g.insertEdge(4,5,1);
g.insertEdge(3,4,1);
g.insertEdge(5,3,1);
cout << "Minimum weight cycle in the graph is: "<<g.FindMinCycle() << endl;
return 0;
}
Simply use std::ifstream to open the file, and then use operator>> to read the values from it (it will handle skipping all of the whitespace in between the values), eg:
#include <iostream>
#include <fstream>
using namespace std;
...
int main()
{
int V, u, v, w;
ifstream ifs("filename.txt");
if (!ifs.is_open())
{
cerr << "Cannot open the file" << endl;
return 0;
}
ifs >> V;
Graph g(V);
while (ifs >> u >> v >> w) {
g.insertEdge(u, v, w);
}
cout << "Minimum weight cycle in the graph is: " << g.FindMinCycle() << endl;
return 0;
}
Related
I am attempting an online coding challenge wherein I am to implement a pathfinding algorithm that finds the shortest path between two points on a 2D grid. The code that is submitted is tested against a number of test cases that I, unfortunately, am unable to see, but it will however tell me if my answer for shortest distance is correct or not. My implementation of the A* algorithm returns a correct answer on 2/3 test cases and I cannot seem to figure out what scenario might create an incorrect answer on the third?
I have tried several of my own test cases and have gotten correct answers for all of those and at this point am feeling a little bit lost. There must be something small in my code that I am not seeing that is causing this third case to fail.
More details
The grid is w by h and contains only 1's (passable) and 0's (impassable) with every edge having a cost of 1 and the pathway cannot move diagonally
It all starts with the FindPath function which is to return the length of the shortest path, or -1 if no path is available
pOutBuffer is used to contain the path taken from beginning to end (excluding the starting point). If multiple paths are available then any will be accepted. So it isnt looking for one path in particular
I know the issue is not the result of time or memory inefficiency. I has to be either the distance returned is incorrect, or the values in pOutBuffer are incorrect.
Any help would be greatly appreciated as I am just about out of ideas as to what could possibly be wrong here. Thank you.
#include <set>
#include <vector>
#include <tuple>
#include <queue>
#include <unordered_map>
inline int PositionToIndex(const int x, const int y, const int w, const int h)
{
return x >= 0 && y >= 0 && x < w && y < h? x + y * w : -1;
}
inline std::pair<int, int> IndexToPosition(const int i, const int w)
{
return std::make_pair<int, int>(i % w, i / w);
}
inline int Heuristic(const int xa, const int ya, const int xb, const int yb)
{
return std::abs(xa - xb) + std::abs(ya - yb);
}
class Map
{
public:
const unsigned char* mapData;
int width, height;
const std::vector<std::pair<int, int>> directions = { {1,0}, {0,1}, {-1,0}, {0,-1} };
Map(const unsigned char* pMap, const int nMapWidth, const int nMapHeight)
{
mapData = pMap;
width = nMapWidth;
height = nMapHeight;
}
inline bool IsWithinBounds(const int x, const int y)
{
return x >= 0 && y >= 0 && x < width && y < height;
}
inline bool IsPassable(const int i)
{
return mapData[i] == char(1);
}
std::vector<int> GetNeighbours(const int i)
{
std::vector<int> ret;
int x, y, neighbourIndex;
std::tie(x, y) = IndexToPosition(i, width);
for (auto pair : directions)
{
neighbourIndex = PositionToIndex(x + pair.first, y + pair.second, width, height);
if (neighbourIndex >= 0 && IsWithinBounds(x + pair.first, y + pair.second) && IsPassable(neighbourIndex))
ret.push_back(neighbourIndex);
}
return ret;
}
};
int FindPath(const int nStartX, const int nStartY,
const int nTargetX, const int nTargetY,
const unsigned char* pMap, const int nMapWidth, const int nMapHeight,
int* pOutBuffer, const int nOutBufferSize)
{
int ret = -1;
// create the map
Map map(pMap, nMapWidth, nMapHeight);
// get start and end indecies
int targetIndex = PositionToIndex(nTargetX, nTargetY, nMapWidth, nMapHeight);
int startIndex = PositionToIndex(nStartX, nStartY, nMapWidth, nMapHeight);
// if start and end are same exit
if (targetIndex == startIndex) return 0;
std::unordered_map<int, int> pathway = { {startIndex, startIndex} };
std::unordered_map<int, int> distances = { {startIndex, 0} };
// queue for indecies to process
typedef std::pair<int, int> WeightedLocation;
std::priority_queue<WeightedLocation, std::vector<WeightedLocation>, std::greater<WeightedLocation>> queue;
queue.emplace(0, startIndex);
while (!queue.empty())
{
int currentWeight, currentIndex;
std::tie(currentWeight, currentIndex) = queue.top();
queue.pop();
if (currentIndex == targetIndex)
break;
int newDistance = distances[currentIndex] + 1;
for (int n : map.GetNeighbours(currentIndex))
{
if (distances.find(n) == distances.end() || newDistance < distances[n])
{
distances[n] = newDistance;
int weight = newDistance + Heuristic(n % nMapWidth, n / nMapWidth, nTargetX, nTargetY);
queue.emplace(weight, n);
pathway[n] = currentIndex;
}
}
}
if (pathway.find(targetIndex) != pathway.end())
{
int current = targetIndex;
while (current != startIndex)
{
int outIndex = distances[current] - 1;
pOutBuffer[distances[current] - 1] = current;
current = pathway[current];
}
ret = distances[targetIndex];
}
return ret;
}
EDIT: Reposting; tried the problem a bit more and reposting. I'm genuinely lost at this point.
Have this small graph problem to do for today, was wondering if anyone had any possible solutions/insight for it.
Given two containers, one of which can accommodate a liters of water and the other b liters of water, determine the number of steps required to obtain exactly target liters of water in one of the containers, or -1 if it cannot be done.
At the beginning both containers are empty. The following operations are counted as 'steps':
Empty a contain er
Fill a container
Pour water from one container to the other, without spilling, until one of the containers is either full or empty
To get you started, we provide you a Node and a Graph class. Your job is to implement the function Graph createGraph(int capacity_a, int capacity_b), which builds a graph that contains all the possible container states given the capacity of two containers, and int findSolution(Graph g, int target), which does the graph traversal and returns the minimum steps.
You can modify the structures if you like, or build your own solution without using the provided structures. We will only test your int waterPouring(int a, int b, int target) function.
Hint: Which algorithm guarantees a minimun number of steps to reach a target?
My initial guess was Dijkstra's for the findSolution(), and attempted to translate some pseudo-code, but that didn't work out. I (think) I implemented createGraph correctly. Quite unsure as to where to go/if there's a better way to do this. Here's the code:
Thank you!
waterPouring.cpp:
#include <unordered_map>
#include <queue>
#include <vector>
using namespace std;
#define EMPTY 0
class Node {
public:
int a;
int b;
vector<Node *> neighbors;
Node () : a(EMPTY), b(EMPTY), neighbors() {}
Node (const int &a_, const int &b_) : a(a_), b(b_), neighbors() {}
Node (const int &a_, const int &b_, const vector<Node *> &neighbors_) : a(a_), b(b_), neighbors(neighbors_) {}
Node (const Node &tmpNode) : a(tmpNode.a), b(tmpNode.b), neighbors() {}
bool operator==(const Node & b_node)
{
return a == b_node.a && b == b_node.b;
}
Node &operator=(const Node & b_node) {
// WARNING: This operator does not copy the vector
a = b_node.a;
b = b_node.b;
return *this;
}
};
struct Graph {
vector<Node *> nodes;
};
Graph createGraph(int capacity_a, int capacity_b) {
// TODO
Graph g;
Node * capacityNode = new Node(capacity_a, capacity_b);
for (int i = 0; i < g.nodes.size(); i++) {
g.nodes.push_back(capacityNode);
}
return g;
}
int findSolution(Graph g, int target) {
// TODO: returns minimum number of steps to reach target liters of
water (or -1)
for (int& node : g) {
// not sure
}
return -1;
}
int waterPouring(int a, int b, int target) {
// Call createGraph
// Call findSolution
Graph stateMachineGraph = createGraph(a, b);
int steps = findSolution(stateMachineGraph, target);
for (Node *graphNode : stateMachineGraph.nodes)
{
delete graphNode;
}
return steps;
}
If you are ok with a solution without using a graph (provided task description allows it), you can do the following:
Suppose you have two containers with capacities a and b and you need to get c liters in the end.
Suppose you have two containers with capacities a and b and you need to get c liters in the end.
First observe, that every operation you are allowed to perform moves x * a + y * b liters of water. E.g. if you are pouring water from the full second to the full first container you are pouring 1 * b - 1 * a. You can continue to persuade yourself that this is true. This gives us the following equation:
x * a + y * b = c
This is a diophantine equation and it has a solution if gcd(a, b) divides c (see Bézout's identity). You can solve it using extended Euclidean algorithm. If c is less than max(a, b) then either x or y less than zero. Suppose x > 0. Then you need to full a-container x times, pour water from it to b-container emptying it y times.
Example: a = 9, b = 5, c = 6. We have
-1 * 9 + 3 * 5 = 6
So, we need to
0 5 // Full the second (1)
5 0 // Pour to the first
5 5 // Full the second (2)
9 1 // Pour to the first
0 1 // Empty the first (-1)
1 0 // Pour to the first
1 5 // Full the second (3)
6 0 // Pour to the first
But if you really want to use graph, then
#include <iostream>
#include <algorithm>
#include <numeric>
#include <vector>
#include <queue>
struct Node { int a, b; };
class Graph {
public:
std::vector<std::pair<Node, std::vector<int>>> nodes;
static Graph Create(int a, int b) {
auto index = [a,b](int i, int j) {
return i * (b + 1) + j;
};
Graph g;
for (int i = 0; i <= a; ++i) {
for (int j = 0; j <= b; ++j) {
std::vector<int> adj;
if (i < a) adj.push_back(index(a, j));
if (i > 0) adj.push_back(index(0, j));
if (j < b) adj.push_back(index(i, b));
if (j > 0) adj.push_back(index(i, 0));
int da = std::min(a - i, j);
int db = std::min(b - j, i);
adj.push_back(index(i + da, j - da));
adj.push_back(index(i - db, j + db));
std::sort(adj.begin(), adj.end());
adj.erase(std::unique(adj.begin(), adj.end()), adj.end());
g.nodes.push_back({ { i,j }, adj });
}
}
return g;
}
// Breadth-first search
std::pair<int, std::vector<int>> Shortest(int target) const {
std::vector<bool> visited(nodes.size(), 0);
std::vector<int> dist(nodes.size(), std::numeric_limits<int>::max());
std::vector<int> prev(nodes.size(), -1);
std::queue<int> q;
int cur_dist = 0;
q.push(0); visited[0] = true; dist[0] = 0;
while (q.size() > 0) {
int index = q.front(); q.pop();
for (auto i : nodes[index].second) {
if (nodes[i].first.a == target || nodes[i].first.b == target) {
int j = index;
std::vector<int> path = { i, index };
while (prev[j] != -1) {
path.push_back(j = prev[j]);
}
return { dist[index] + 1, path };
}
if (!visited[i]) {
q.push(i); visited[i] = true; dist[i] = dist[index] + 1; prev[i] = index;
}
}
}
return { -1, {} };
}
};
int main()
{
const auto g = Graph::Create(9, 5);
const auto p = g.Shortest(6);
for (int i = (int)p.second.size() - 1; i >= 0; --i) {
std::cout << g.nodes[p.second[i]].first.a << " " << g.nodes[p.second[i]].first.b << std::endl;
}
std::cout << std::endl << p.first << std::endl;
return 0;
}
The output is the same:
0 0
0 5
5 0
5 5
9 1
0 1
1 0
1 5
6 0
8
I have a map of pairs map<pair<int, int>, int> counts. I am iterating through this and I am calculating some float numbers, if the value of the map is bigger than 2. Now I want to save the float numbers in a vector that is as big as the number of similar values in my first element of my pair.
An example of values in my map:
1 1 4
1 2 5
1 3 5
1 7 5
1 29 2 .. now the first key changes so the size would be 4 (not 5, because one value is < 3)
2 10 1
2 20 4
...
I tried to make a minimal example, which explains it a little bit better:
int main(int argc, char** argv)
{
// Create some values - both vectors have the same size
vector<pair<int, int>> pairs;
pairs.push_back(make_pair(1, 1));
pairs.push_back(make_pair(1, 2));
pairs.push_back(make_pair(1, 4));
pairs.push_back(make_pair(2, 7));
pairs.push_back(make_pair(2, 4));
pairs.push_back(make_pair(3, 5));
pairs.push_back(make_pair(3, 7));
pairs.push_back(make_pair(3, 8));
vector<float> value;
value.push_back(4);
value.push_back(5);
value.push_back(8);
value.push_back(2);
value.push_back(5);
value.push_back(6);
value.push_back(7);
value.push_back(8);
map<pair<int, int>, int> counts;
vector<vector<vector<float>>> descriptors_1, descriptors_2;
vector<float> oa_simil;
float Overall_Similarity;
for (size_t i = 0; i < pairs.size(); i++) {
counts.insert(make_pair(make_pair(pairs[i].first, pairs[i].second), value[i]));
}
for (const auto& p : counts) {
const auto& p1 = p.first.first;
const auto& p2 = p.first.second;
int count = p.second;
if (p.second >= 3) {
float S = 0;
// Two for-loops that calculate a new S for every p1, p2 combination >= 3
//S = ls_1;
// 5 Linesegments in image 1 are compared to 5 Linesegments in image 2, if p.second >= 3
for (int ls_1 = 0; ls_1 < 5; ls_1++) {
for (int ls_2 = 0; ls_2 < 5; ls_2++) {
pair<int, int> index_1, index_2;
index_1 = make_pair(p1, ls_1);
index_2 = make_pair(p2, ls_2);
// Calculate the Similarity of different pairs of line segments in a complex function
calculateSimilarity(Overall_Similarity, descriptors_1, descriptors_2, index_1, index_2);
oa_simil.push_back(Overall_Similarity);
}
// Get the maximum similarity of every single line segment in one image
float max_M = *max_element(oa_simil.begin(), oa_simil.end());
oa_simil.clear();
// Sum up the maxima -> S contains the maximum for the combination of two Linesegments p1, p2 (if p >=3)
S += max_M;
}
// Now I want to get the maximum value S for every single p1
// In the example I want the maximum S out of e.g. the 3 pairs (1,1), (1,2), (1,4).
// Trying to push the S in a vector<float> when p1 changes.
}
}
}
I already tried to get all my keys and the value of S into a new map but that seems like a waste of computation time when I am already iterating through my keys.
I think I solved it. I created a map for my pairs and S values and inversed the result to make it possible looking for the biggest value. Then I compared my p1 with the p1 of the previous iteration to test whether this value changed.
template<typename A, typename B>
std::pair<B, A> flip_pair(const std::pair<A, B> &p)
{
return std::pair<B, A>(p.second, p.first);
}
template<typename A, typename B>
std::multimap<B, A> flip_map(const std::map<A, B> &src)
{
std::multimap<B, A> dst;
std::transform(src.begin(), src.end(), std::inserter(dst, dst.begin()),
flip_pair<A, B>);
return dst;
}
int main(int argc, char** argv)
{
map<pair<int, int>, float> similarities;
multimap<float, pair<int, int>> inversed_map;
...
// Get the maximum similarity of every single line segment in one image
float max_M = *max_element(oa_simil.begin(), oa_simil.end());
oa_simil.clear();
// Sum up the maxima -> S contains the maximum for the combination of two Linesegments p1, p2 (if p >=3)
S += max_M;
}
similarities.insert(make_pair(make_pair(p1, p2), S));
inversed_map = flip_map(similarities);
if ((p1 > p1_old) && ((int)inversed_map.size() > 0)) {
// Sort the vector and keep track of the indexes of the pair.
auto it = inversed_map.rbegin();
cout << it->second.first << " " << it->second.second << " " << it->first << endl;
similarities.clear();
inversed_map.clear();
}
p1_old = p1;
}
}
}
Suppose we are given a weighted tree and some set of vertices of that tree. The problem is to isolate those vertices(e.g. there should not be paths between them) by removing edges from the tree, such that the sum of the weights of removed edges is minimal.
I have been trying to solve this problem for about two hours, then I found solution in C++, but there was no explanation. As I understood it uses Dynamic Programming technique.
My question is what is the basic idea of solving this problem?
Thanks!
Here is the solution.
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
#define PB push_back
#define MP make_pair
typedef long long LL;
const int MAXN = 100005;
const LL inf = 1LL << 55;
int N, K;
bool bad[MAXN];
LL dp[MAXN][2];
vector<pair<int, int> > adj[MAXN];
void dfs(int u, int fa) {
dp[u][1] = 0;
dp[u][0] = bad[u] ? inf : 0;
for (int i = 0; i < adj[u].size(); i++) {
int v = adj[u][i].first, w = adj[u][i].second;
if (v != fa) {
dfs(v, u);
dp[u][1] += min(dp[v][0], dp[v][1] + w);
dp[u][1] = min(dp[u][1], dp[u][0] + dp[v][1]);
if (!bad[u])
dp[u][0] += min(dp[v][0], dp[v][1] + w);
}
}
}
int main() {
cin >> N >> K;
for (int i = 1; i < N; i++) {
int a, b, c;
cin >> a >> b >> c;
adj[a].PB(MP(b, c)); adj[b].PB(MP(a, c));
}
memset(bad, false, sizeof(bad));
for (int i = 0; i < K; i++) {
int x;
cin >> x;
bad[x] = true;
}
dfs(0, -1);
LL ret = min(dp[0][0], dp[0][1]);
cout << ret << endl;
return 0;
}
Formally, the problem is, given an acyclic undirected graph with weighted edges, together with a set of terminal vertices, find the minimum set of edges whose removal means that, for all pairs of distinct terminals, those terminals are no longer connected.
Root the graph arbitrarily and treat it as a general tree. We run a dynamic program that operates bottom-up on the vertices. Each vertex u has two labels: dp[u][0] is the minimum weight of edges to be removed in the subtree rooted at u so that no terminal in the subtree is connected to another terminal or to u, and dp[u][1] is the minimum removal weight that leaves exactly one subtree terminal connected to u. We have a recurrence
{ infinity if v is a terminal
dp[u][0] = { sum dp[v][0] otherwise
{ children v of u
dp[u][1] = min dp[v][1] + sum min(dp[w][0], dp[w][1] + weight(uw)),
children v of u other children w of v
taking an empty min to be infinity.
We have a matrix of given integers (any from 1 to INT_MAX) like
1 2 3
1 3 3
1 3 3
100 2 1
We want to create polygons with same colors for each unique int in matrix so our polygons would have coords/groupings like shown here.
and we could generate image like this:
Which *(because of vectirisation that was performed would scale to such size like):
(sorry for crappy drawings)
Is it possible and how to do such thing with boost geometry?
Update:
So #sehe sad: I'd simply let Boost Geometry do most of the work. so I created this pixel by pixel class aeria grower using purely Boost.Geometry, compiles, runs but I need it to run on clustered data.. and I have 1000 by 1800 files of uchars (each unique uchar == data belongs to that claster). Problem with this code: on 18th line it gets SO WARY SLOW that each point creation starts to take more than one second=(
code:
//Boost
#include <boost/assign.hpp>
#include <boost/foreach.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/multi/geometries/multi_polygon.hpp>
#include <boost/geometry/geometries/adapted/boost_tuple.hpp>
//and this is why we use Boost Geometry from Boost trunk
//#include <boost/geometry/extensions/io/svg/svg_mapper.hpp>
BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian)
void make_point(int x, int y, boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > & ring)
{
using namespace boost::assign;
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x-1, y-1));
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x, y-1));
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x, y));
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x-1, y));
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x-1, y-1));
boost::geometry::correct(ring);
}
void create_point(int x, int y, boost::geometry::model::multi_polygon< boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > > & mp)
{
boost::geometry::model::multi_polygon< boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > > temp;
boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > ring;
make_point(x, y, ring);
boost::geometry::union_(mp, ring, temp);
boost::geometry::correct(temp);
mp=temp;
}
int main()
{
using namespace boost::assign;
boost::geometry::model::multi_polygon< boost::geometry::model::polygon < boost::geometry::model::d2::point_xy<double> > > pol, simpl;
//read image
std::ifstream in("1.mask", std::ios_base::in | std::ios_base::binary);
int sx, sy;
in.read(reinterpret_cast<char*>(&sy), sizeof(int));
in.read(reinterpret_cast<char*>(&sx), sizeof(int));
std::vector< std::vector<unsigned char> > image(sy);
for(int i =1; i <= sy; i++)
{
std::vector<unsigned char> row(sx);
in.read(reinterpret_cast<char*>(&row[0]), sx);
image[i-1] = row;
}
//
std::map<unsigned char, boost::geometry::model::multi_polygon < boost::geometry::model::polygon < boost::geometry::model::d2::point_xy<double> > > > layered_image;
for(int y=1; y <= sy; y++)
{
for(int x=1; x <= sx; x++)
{
if (image[y-1][x-1] != 1)
{
create_point(x, y, layered_image[image[y-1][x-1]]);
std::cout << x << " : " << y << std::endl;
}
}
}
}
So as you can see my code suks.. so I decided to create a renderer for #sehe code:
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <set>
//Boost
#include <boost/assign.hpp>
#include <boost/array.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/multi/geometries/multi_polygon.hpp>
#include <boost/geometry/geometries/adapted/boost_tuple.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/mersenne_twister.hpp>
//and this is why we use Boost Geometry from Boost trunk
#include <boost/geometry/extensions/io/svg/svg_mapper.hpp>
BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian)
namespace mxdetail
{
typedef size_t cell_id; // row * COLS + col
template <typename T> struct area
{
T value;
typedef std::vector<cell_id> cells_t;
cells_t cells;
};
template <typename T, size_t Rows, size_t Cols>
std::vector<area<T> > getareas(const boost::array<boost::array<T, Cols>, Rows>& matrix)
{
typedef boost::array<boost::array<T, Cols>, Rows> mtx;
std::vector<area<T> > areas;
struct visitor_t
{
const mtx& matrix;
std::set<cell_id> visited;
visitor_t(const mtx& mtx) : matrix(mtx) { }
area<T> start(const int row, const int col)
{
area<T> result;
visit(row, col, result);
return result;
}
void visit(const int row, const int col, area<T>& current)
{
const cell_id id = row*Cols+col;
if (visited.end() != visited.find(id))
return;
bool matches = current.cells.empty() || (matrix[row][col] == current.value);
if (matches)
{
visited.insert(id);
current.value = matrix[row][col];
current.cells.push_back(id);
// process neighbours
for (int nrow=std::max(0, row-1); nrow < std::min((int) Rows, row+2); nrow++)
for (int ncol=std::max(0, col-1); ncol < std::min((int) Cols, col+2); ncol++)
/* if (ncol!=col || nrow!=row) */
visit(nrow, ncol, current);
}
}
} visitor(matrix);
for (int r=0; r < (int) Rows; r++)
for (int c=0; c < (int) Cols; c++)
{
mxdetail::area<int> area = visitor.start(r,c);
if (!area.cells.empty()) // happens when startpoint already visited
areas.push_back(area);
}
return areas;
}
}
typedef boost::array<int, 4> row;
template <typename T, size_t N>
boost::array<T, N> make_array(const T (&a)[N])
{
boost::array<T, N> result;
std::copy(a, a+N, result.begin());
return result;
}
void make_point(int x, int y, boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > & ring)
{
using namespace boost::assign;
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x-1, y-1));
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x, y-1));
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x, y));
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x-1, y));
boost::geometry::append( ring, boost::geometry::model::d2::point_xy<double>(x-1, y-1));
boost::geometry::correct(ring);
}
void create_point(int x, int y, boost::geometry::model::multi_polygon< boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > > & mp)
{
boost::geometry::model::multi_polygon< boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > > temp;
boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > ring;
make_point(x, y, ring);
boost::geometry::union_(mp, ring, temp);
boost::geometry::correct(temp);
mp=temp;
}
boost::random::mt19937 rng;
boost::random::uniform_int_distribution<> color(10,255);
std::string fill_rule()
{
int red, green, blue;
red = color(rng);
green = color(rng);
blue = color(rng);
std::ostringstream rule;
rule << "fill-rule:nonzero;fill-opacity:0.5;fill:rgb("
<< red << "," << green << "," << blue
<< ");stroke:rgb("
<< (red - 5) << "," << (green - 5) << "," << (blue -5)
<< ");stroke-width:2";
return rule.str();
}
int main()
{
int sx = 4;
int sy = 5;
int row0[] = { 1 , 2, 3, 3, };
int row1[] = { 1 , 3, 3, 3,};
int row2[] = { 1 , 3, 3, 3, };
int row3[] = { 2 , 2, 1, 2, };
int row4[] = { 100, 2, 2, 2, };
boost::array<row, 5> matrix;
matrix[0] = make_array(row0);
matrix[1] = make_array(row1);
matrix[2] = make_array(row2);
matrix[3] = make_array(row3);
matrix[4] = make_array(row4);
typedef std::vector<mxdetail::area<int> > areas_t;
typedef areas_t::value_type::cells_t cells_t;
areas_t areas = mxdetail::getareas(matrix);
using namespace boost::assign;
typedef boost::geometry::model::polygon
<
boost::geometry::model::d2::point_xy<double>
> polygon;
typedef boost::geometry::model::multi_polygon<polygon> mp;
typedef boost::geometry::point_type<mp>::type point_type;
std::string filename = "draw.svg";
std::ofstream svg(filename.c_str());
boost::geometry::svg_mapper<point_type> mapper(svg, 400, 400);
for (areas_t::const_iterator it=areas.begin(); it!=areas.end(); ++it)
{
mp pol;
std::cout << "area of " << it->value << ": ";
for (cells_t::const_iterator pit=it->cells.begin(); pit!=it->cells.end(); ++pit)
{
int row = *pit / 3, col = *pit % 3;
std::cout << "(" << row << "," << col << "), ";
create_point( (row+1), (col+1), pol);
}
std::cout << std::endl;
mapper.add(pol);
mapper.map(pol, fill_rule());
}
std::cout << "areas detected: " << areas.size() << std::endl;
std::cin.get();
}
this code is compilable but it sucks (seems I did not get how to work with arrays after all...):
In short, if I got the question right, I'd simply let Boost Geometry do most of the work.
For a sample matrix of NxM, create NxM 'flyweight' rectangle polygons to correspond to each matrix cell visually.
Now, using an iterative deepening algorithm, find all groups:
* for each _unvisited_ cell in matrix
* start a new group
* [visit:]
- mark _visited_
- for each neighbour with equal value:
- add to curent group and
- recurse [visit:]
Note that the result of this algorithm could be distinct groups with the same values (representing disjunct polygons). E.g. the value 2 from the sample in the OP would result in two groups.
Now for each group, simply call Boost Geometry's Union_ algorithm to find the consolidated polygon to represent that group.
Sample implementation
Update Here is a non-optimized implementation in C++11:
Edit See here for C++03 version (using Boost)
The sample data used in the test corresponds to the matrix from the question.
#include <iostream>
#include <array>
#include <vector>
#include <set>
namespace mxdetail
{
typedef size_t cell_id; // row * COLS + col
template <typename T> struct area
{
T value;
std::vector<cell_id> cells;
};
template <typename T, size_t Rows, size_t Cols>
std::vector<area<T> > getareas(const std::array<std::array<T, Cols>, Rows>& matrix)
{
typedef std::array<std::array<T, Cols>, Rows> mtx;
std::vector<area<T> > areas;
struct visitor_t
{
const mtx& matrix;
std::set<cell_id> visited;
visitor_t(const mtx& mtx) : matrix(mtx) { }
area<T> start(const int row, const int col)
{
area<T> result;
visit(row, col, result);
return result;
}
void visit(const int row, const int col, area<T>& current)
{
const cell_id id = row*Cols+col;
if (visited.end() != visited.find(id))
return;
bool matches = current.cells.empty() || (matrix[row][col] == current.value);
if (matches)
{
visited.insert(id);
current.value = matrix[row][col];
current.cells.push_back(id);
// process neighbours
for (int nrow=std::max(0, row-1); nrow < std::min((int) Rows, row+2); nrow++)
for (int ncol=std::max(0, col-1); ncol < std::min((int) Cols, col+2); ncol++)
/* if (ncol!=col || nrow!=row) */
visit(nrow, ncol, current);
}
}
} visitor(matrix);
for (int r=0; r < Rows; r++)
for (int c=0; c < Cols; c++)
{
auto area = visitor.start(r,c);
if (!area.cells.empty()) // happens when startpoint already visited
areas.push_back(area);
}
return areas;
}
}
int main()
{
typedef std::array<int, 3> row;
std::array<row, 4> matrix = {
row { 1 , 2, 3, },
row { 1 , 3, 3, },
row { 1 , 3, 3, },
row { 100, 2, 1, },
};
auto areas = mxdetail::getareas(matrix);
std::cout << "areas detected: " << areas.size() << std::endl;
for (const auto& area : areas)
{
std::cout << "area of " << area.value << ": ";
for (auto pt : area.cells)
{
int row = pt / 3, col = pt % 3;
std::cout << "(" << row << "," << col << "), ";
}
std::cout << std::endl;
}
}
Compiled with gcc-4.6 -std=c++0x the output is:
areas detected: 6
area of 1: (0,0), (1,0), (2,0),
area of 2: (0,1),
area of 3: (0,2), (1,1), (1,2), (2,1), (2,2),
area of 100: (3,0),
area of 2: (3,1),
area of 1: (3,2),
When number of points is big (say, more than 1000x1000), the solution above would gobble a lot of memory. And this is exactly what happened to the topic-starter.
Below I show more scalable approach.
I would separate two problems here: one is to find the areas, another is to convert them into polygons.
The first problem is actually equivalent to finding the connected components of the grid graph where neighbors has edges if and only if they have equal "colors" attached to it. One can use a grid graph from boost-graph.
#include <boost/graph/grid_graph.hpp>
// Define dimension lengths, a MxN in this case
boost::array<int, 2> lengths = { { M, N } };
// Create a MxN two-dimensional, unwrapped grid graph
grid_graph<2> graph(lengths);
Next, we should convert a given matrix M into an edge filter: grid edges are present iff the "color" of the neighbors are the same.
template <class Matrix>
struct GridEdgeFilter
{
typedef grid_graph<2> grid;
GridEdgeFilter(const Matrix & m, const grid&g):_m(&m),_g(&g){}
/// \return true iff edge is present in the graph
bool operator()(grid::edge_descriptor e) const
{
grid::vertex_descriptor src = source(e,*_g), tgt = target(e,*_g);
//src[0] is x-coord of src, etc. The value (*m)[x,y] is the color of the point (x,y).
//Edge is preserved iff matrix values are equal
return (*_m)[src[0],src[1]] == (*_m)[tgt[0],tgt[1]];
}
const Matrix * _m;
const grid* _g;
};
Finally, we define a boost::filtered_graph of grid and EdgeFilter and call Boost.Graph algorithm for connected components.
Each connected component represents a set of points of a single color i.e. exactly the area we want to transform into a polygon.
Here we have another issue. Boost.Geometry only allows to merge polygons one by one. Hence it becomes very slow when number of polygons is big.
The better way is to use Boost.Polygon, namely its Property Merge functionality. One starts with empty property_merge object, and goes on by inserting rectangles of given color (you can set color as a property). Then one calls the method merge and gets a polygon_set as the output.