Codeforces question on Binary Lifting and LCA - c++

Recently, I was solving this problem on Codeforces, but I failed the testcases. I can't see the testcases, nor can I see other's solutions because it is a gym question. Since, I cannot view the testcases where my code is failing, I need some help. I promise that the code is quite readable. Question is mainly on Binary lifting and LCA calculation.
As probably know, sloths live on trees. Accordingly, David has a pet sloth which he lets play on his unweighted trees when he solves programming problems. Occasionally, David will notice that his sloth is located on a particular node 𝑎 in the tree, and ask it to move to some other node 𝑏.
Of course, the sloth is as well-intentioned as can be, but alas, it only has enough energy to move across at most 𝑐 edges. If the sloth needs to cross fewer than 𝑐 edges to get to node 𝑏, it will get there and then take a nap. Otherwise, it will get as close as possible before it retires and hangs idly awaiting further digestion.
Where will the sloth end up? Also, since this happens quite often, David would like you to answer 𝑞 queries, each one of the similar form.
Input
The first line will contain a single integer 𝑛, the number of nodes in the tree. The following 𝑛−1 lines will contain two integers 𝑢 and 𝑣, describing the edges in the tree. These edges will form a tree.
After that, there will be a single line containing an integer 𝑞, the number of times David will motivate his sloth to move. 𝑞 lines follow, each containing three integers 𝑎, 𝑏, and 𝑐: the node the sloth starts on, the node David asks the sloth to move to, and the energy the sloth has when starting.
int LOGN = 19;
int n;
int timer = 0;
vector<int> adj[300005];
bool vis[300005];
int p[300005], tin[300005], tout[300005];
vector<int> d(300005,0);
int dp[300005][20];
void dfs(int v, int parent)
{
tin[v] = ++timer;
if (parent != -1)
d[v] = d[parent] + 1;
p[v] = parent;
dp[v][0] = parent;
for (int i = 1; i < LOGN; i++){
if (dp[v][i — 1] == -1)
dp[v][i] = -1;
else
dp[v][i] = dp[dp[v][i — 1]][i — 1];
}
for (auto u : adj[v]){
if (u != parent)
dfs(u, v);
}
tout[v] = ++timer;
}
bool isAncestor(int p, int c){ // check if p is ancestor of c
return (tin[p] <= tin[c] && tout[p] >= tout[c]);
}
int lift(int v, int steps) //find the y-th ancestor of x
{
for (int i = 0; i < 19; ++i){
if ((1 << i) & steps){
v = dp[v][i];
if (v == -1)
return -1;
steps -= (1 << i);
}
}
return v;
}
int LCA(int u, int v)
{
if (isAncestor(u, v))
return u;
if (isAncestor(v, u))
return v;
for (int i = 18; i >= 0; i--)
{
if (dp[u][i] == -1)// -1 should not be passed
continue;
if (!isAncestor(dp[u][i], v))
u = dp[u][i];
}
return dp[u][0];
}
void solve()
{
cin>>n;
timer=0;
for(int i=0;i<n-1;i++){// Input
int x,y;
cin>>x>>y;
x--;y--;
adj[x].pb(y);
adj[y].pb(x);
}
dfs(0,-1);
tout[0]=++timer;
int q;
cin>>q;
for(int z=0;z<q;z++){
int u,v,energy;
cin>>u>>v>>energy;
u--;v--;
if(isAncestor(u,v))// u is ancestor of v
{
if(d[v]-d[u]<=energy)// reachable
cout<<v+1<<endl;
else{// midway: lift
int res=lift(v,energy);
cout<<res+1<<endl;
}
}
else if(isAncestor(v,u))// v is ancestor of u
{
if(d[u]-d[v]<=energy)// reachable
cout<<v+1<<endl;
else{/// midway: lift
int res=lift(u,d[u]-d[v]-energy);
cout<<res+1<<endl;
}
}
else{
int lca=LCA(u,v);
int dist=d[u]+d[v]-2*d[lca];
if(energy>=dist)
cout<<v+1<<endl;
else{
// find vertex of the path from u to v with distance of energy from u
int dist1=d[u]-d[lca];
int dist2=d[v]-d[lca];
// lies on the path from u to LCA
if(energy<=dist1){
int res=lift(u,energy)+1;
cout<<res<<endl;
}
// lies on the path from LCA to v
else{
int rem=energy-dist1;
dist2-=rem;
int res=lift(v,dist2)+1;
cout<<res<<endl;
}
}
}
}
}
signed main()
{
FAST;
solve();
return 0;
}

Related

Getting SIGSEGV (segmentation error) for the given problem. (Finding LCA of a generic tree)

So, I was trying to solve the below problem using the most basic method i.e. storing the paths and finding LCA.
My code is working fine on VSCode and giving the right output. But when submitting on SPOJ, it gives runtime error (SIGSEGV).
Problem Link: https://www.spoj.com/problems/LCA/
Problem Description:
A tree is an undirected graph in which any two vertices are connected by exactly one simple path. In other words, any connected graph without cycles is a tree. - Wikipedia
The lowest common ancestor (LCA) is a concept in graph theory and computer science. Let T be a rooted tree with N nodes. The lowest common ancestor is defined between two nodes v and w as the lowest node in T that has both v and w as descendants (where we allow a node to be a descendant of itself). - Wikipedia
Your task in this problem is to find the LCA of any two given nodes v and w in a given tree T.
Sample Input:
1
7
3 2 3 4
0
3 5 6 7
0
0
0
0
2
5 7
2 7
Sample Output:
Case 1:
3
1
My Code:
#include <iostream>
#include <vector>
#include <cmath>
#include <cstring>
using namespace std;
vector<vector<int>> edges;
bool storepath(int s, int d, vector<int>& path, vector<bool>& visited) {
if(s == d)
{
path.push_back(d);
return true;
}
else if(edges[s].size() == 1) {
if(s != d)
{
for(int i = 0; i < path.size(); i++)
if(path[i] == s) {
path.erase(path.begin() + i);
}
}
return false;
}
visited[s] = true;
path.push_back(s);
for(auto e: edges[s])
{
if(visited[e] == false)
{
bool ans = storepath(e, d, path, visited);
if(ans)
break;
}
}
}
int LCA(int a, int b)
{
if(a == b)
return a;
vector<int> path1, path2;
vector<bool> visited(edges.size(), false);
storepath(1, a, path1, visited);
visited.assign(edges.size(), false);
storepath(1, b, path2, visited);
int n = path1.size();
int m = path2.size();
int i = 0,j = 0;
while(i < n && j < m && path1[i] == path2[j]) {
i++;
j++;
}
return path1[i-1];
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int t;
cin >> t;
int Case = 1;
while(t--)
{
int n;
cin >> n;
edges.resize(n+1);
for(int i = 1; i <= n; i++)
{
int size, val;
cin >> size;
while(size--)
{
cin >> val;
edges[i].push_back(val);
edges[val].push_back(i);
}
}
int q;
cin >> q;
cout << "Case "<< Case << ":" << endl;
while(q--)
{
int a, b;
cin >> a >> b;
cout << LCA(a, b) << endl;
}
Case++;
edges.clear(); //added after igor's comment (forgot to add before but used while submitting)
}
return 0;
}
I think I'm not accessing any out of scope element so SIGSEGV should not occur.
Please tell me how can I fix and improve my code.
Some bugs are easy to find, when you know how to find them. The tools every programmer should know about are valgrind and -fsanitize. Remember to always compile with warnings enabled and fix them. Compiling your code with:
g++ -Wall -Wextra -fsanitize=undefined 1.cpp && ./a.out </tmp/2
results in a helpful warning:
1.cpp:38:1: warning: control reaches end of non-void function [-Wreturn-type]
38 | }
| ^
and a runtime error:
1.cpp:9:6: runtime error: execution reached the end of a value-returning function without returning a value
Your storepath doesn't return value.

Trying to use Minimum Spanning Tree example from the book but It doesn't work with large data

Note: This is not my code
I am trying to use the data structures with C++ textbook's Minimum Spanning Tree Algorithm but as you can see I made a edges[] array of edges and commented out the old edges[] array but It looks like it doesn't work for larger amount of edge or something. (I am just using chars as ints by the way)
Does anyone know why? I didn't change a lot, I just changed the edges array.
It compiles just fine but If you run it you will see that it won't work with my data but It will with the original data.
The arrays are right over the main (last thing)
Also if you don't want to open up your ide, here is my code on an online IDE: http://goo.gl/35KMcK
Here is the code:
#include <iostream>
using namespace std;
class MSTEdge
{
char src;
char dest;
int weight;
public:
MSTEdge(char s = 0, char d = 0, int w = 0) : src(s), dest(d), weight(w) { }
char& getSrc() { return src; }
char& getDest() { return dest; }
int& getWeight() { return weight; }
int& get() { return getWeight(); }
};
// undirected and weighted graph
class Graph
{
int V, E;
MSTEdge* edge;
int icount;
public:
Graph(int v, int e) : V(v), E(e), icount(0)
{
edge = new MSTEdge[e];
}
int& getVertexAmount() { return V; }
int& getEdgeAmount() { return E; }
MSTEdge*& getEdges() { return edge; }
MSTEdge& operator [](int x) { return edge[x]; }
void insert(MSTEdge& e)
{
edge[icount++] = e;
}
};
// subset for union-find
class subset
{
int parent;
int rank;
public:
subset(int p = 0, int r = 0) : parent(p), rank(r) {}
int& getTheParent() { return parent; }
int& getTheRank() { return rank; }
};
// find set of an element i
int find(subset* subsets, int i)
{
// find root and make root as parent of i (path compression)
if (subsets[i].getTheParent() != i)
subsets[i].getTheParent() = find(subsets, subsets[i].getTheParent());
return subsets[i].getTheParent();
}
// union of two sets of x and y
void Union(subset* subsets, int x, int y)
{
int x_root = find(subsets, x);
int yroot = find(subsets, y);
// Attach smaller rank tree under root of high rank tree
// (Union by Rank)
if (subsets[x_root].getTheRank() < subsets[yroot].getTheRank())
subsets[x_root].getTheParent() = yroot;
else if (subsets[x_root].getTheRank() > subsets[yroot].getTheRank())
subsets[yroot].getTheParent() = x_root;
// If ranks are same, then make one as root and increment its rank by one
else
{
subsets[yroot].getTheParent() = x_root;
subsets[x_root].getTheRank()++;
}
}
template <typename T>
void partition_array(T* arr, int& i, int& j, T pivot)
{
while (i <= j)
{
while (arr[i].get() < pivot.get())
i++;
while (arr[j].get() > pivot.get())
j--;
if (i <= j)
{
T tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
};
}
template <typename T>
void quickSort_array(T* arr, int left, int right)
{
int i = left, j = right;
T pivot = arr[(left + right) / 2];
// partition
partition_array(arr, i, j, pivot);
// recursion
if (left < j)
quickSort_array(arr, left, j);
if (i < right)
quickSort_array(arr, i, right);
}
// The main function to construct MST
void MST(Graph& graph)
{
int V = graph.getVertexAmount();
MSTEdge* result = new MSTEdge[V]; // Tnis will store the resultant MST
int e = 0; // An index variable, used for result[]
int i = 0; // An index variable, used for sorted edges
quickSort_array(graph.getEdges(), 0, graph.getEdgeAmount());
// Allocate memory for creating V ssubsets
subset* subsets = new subset[V];
// Create V subsets with single elements
for (int v = 0; v < V; ++v)
{
subsets[v].getTheParent() = v;
subsets[v].getTheRank() = 0;
}
// Number of edges to be taken is equal to V-1
while (e < V - 1)
{
// Step 2: Pick the smallest edge. And increment the index
// for next iteration
MSTEdge next_edge = graph[i++];
int x = find(subsets, next_edge.getSrc());
int y = find(subsets, next_edge.getDest());
// If including this edge does't cause cycle, include it
// in result and increment the index of result for next edge
if (x != y)
{
result[e++] = next_edge;
Union(subsets, x, y);
}
// Else discard the next_edge
}
// print the contents of result[] to display the built MST
cout << "Following are the edges in the constructed MST\n";
for (i = 0; i < e; ++i)
cout
<< result[i].getSrc()
<< " -- "
<< result[i].getDest()
<< " == "
<< result[i].getWeight()
<< endl;
return;
}
/* weighted graph
10
0-------- 1
| \ |
6| 5\ |15
| \ |
2 --------3
4
*/
//MSTEdge edges[] = //THIS WORKS
//{
// MSTEdge(0,1,10),
// MSTEdge(0,2,6),
// MSTEdge(0,3,5),
// MSTEdge(1,3,15),
// MSTEdge(2,3,4)
//};
MSTEdge edges[] = // CAUSES PROBLEMS
{
MSTEdge('A','B',5),
MSTEdge('A','C',1),
MSTEdge('B','C',10),
MSTEdge('B','E',13),
MSTEdge('C','D',5),
MSTEdge('D','E',15),
MSTEdge('D','F',10),
MSTEdge('E','F',17)
};
// Driver program to test above functions
int main()
{
int count = sizeof(edges) / sizeof(MSTEdge);
int V = count - 1; // Number of vertices in graph
Graph graph(V, count);
for (int e = 0; e < count; e++)
graph.insert(edges[e]);
MST(graph);
return 1;
}
// Following are the edges in the constructed MST
// 2 -- 3 == 4
// 0 -- 3 == 5
// 0 -- 1 == 10
The subsets array is initialized using this code:
// Create V subsets with single elements
for (int v = 0; v < V; ++v)
{
subsets[v].getTheParent() = v;
subsets[v].getTheRank() = 0;
}
This gives you subsets having parent values from 0 to V-1
The code then tries to find those subsets using this line
int x = find(subsets, next_edge.getSrc());
But your edges have source and destination set to 'A', 'B', 'C' etc. So it will never be able to find anything in subsets. It is probably accessing items outside the array bounds of subsets and causing undefined behaviour.
To fix it, either change your edges array to use 0, 1, 2, as the node IDs (probably easiest), or change the subsets initialize code to set the parents to 'A', 'B', 'C' etc. Note: there may be more places that assume the node IDs start from 0.

Algorithm to build streets in a city with minimum cost?

Question details:
Rashof is the mayor of EMLand. EMLand consists of intersections and streets. There is exactly one path from each intersection to any of the other intersections. Intersections are denoted by positive intergers 1...n.
A construction company has offered Rashof to rebuild all streets of the EMLand, but Rashof can choose at most k of them to be rebuilt. The Construction company has offered a new length for each street which means after the street is rebuilt the length of the street changes.
Now Rashof as the mayor of the city must choose wisely so as to minimize sum of lengths of paths between all pairs of intersections.
Help Rashof!
Algorithm:
Notations: old edge length is L , new length is L' and set of edges E .
Count(C) number of edges(E') whose length is going to decrease i.e. L' < L
If C is less than or equal to K then
        take all edges(E') into account i.e. Update length of all such edges in E
Else
        1 . Sort all edges(E') based on (L'- L) in ascending order
        2 . Sort those edges(E'' ⊆ E') whose (L'-L) is same based on L' in descending order
        3.hoose 1st K edges(E''' ⊆ E') and update length of all such edges in E
Construct Graph G with Edge E and length L
Apply any shortest distance algorithm or DFS to find distance b/w each pair of node .
Implementation of above algorithm using priority queue and Dijkstra algorithm.
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
struct s{
int x;
int y;
int a;
int b;
int c;
};
const int MAX = 100000;
const long long INF = 100000000000000000;
vector< pii > G[MAX];
long long d[MAX];
void dijkstra(long long start) {
int u, v, i, c, w;
priority_queue< pii, vector< pii >, greater< pii > > Q;
for(i=0;i<MAX;i++){
d[i]=INF;
}
Q.push(pii(0, start));
d[start] = 0;
while(!Q.empty()) {
u = Q.top().second; // node
c = Q.top().first; // node cost so far
Q.pop(); // remove the top item.
if(d[u] < c) continue;
for(i = 0; i < G[u].size(); i++) {
v = G[u][i].first; // node
w = G[u][i].second; // edge weight
if(d[v] > d[u] + w) {
d[v] = d[u] + w;
//cout<<d[v];
Q.push(pii(d[v], v));
}
}
}
}
bool func(const s s1,const s s2) { return (s1.c < s2.c); }
bool func2(const s s1,const s s2) { return (s1.b < s2.b); }
int main() {
long long n, e, u, V, w,x,y,a,b,t,i,j,k,res,z=2;
s S;
vector<s> v;
map<pair<int,int>,int> m;
map<pair<int,int>,int>::iterator it;
cin>>t;
while(t--){
cin>>n>>k;
for(i = 1; i <= n; i++) G[i].clear();
v.clear();
m.clear();
for(i=1;i<n;i++){
cin>>x>>y>>a>>b;
if(b<a){
S.x = x;
S.y =y;
S.a=a;
S.b=b;
S.c=b-a;
v.push_back(S);
}
m[make_pair(x,y)]=a;
}
if(v.size()<=k){
for(i=0;i<v.size();i++){
m[make_pair(v[i].x,v[i].y)]=v[i].b;
}
it = m.begin();
for(;it!=m.end();++it){
u = it->first.first;
V = it->first.second;
w = it->second;
G[u].push_back(pii(V, w));
G[V].push_back(pii(u, w));
}
res = 0;
for(i=1;i<=n;i++){
dijkstra(i);
for(j= 1; j <= n; j++) {
if(i == j) continue;
if(d[j] >= INF) ;
else res+=d[j];
}
}
cout<<res/z<<"\n";
}
else{
sort(v.begin(),v.end(),func);
for(i=0;i<v.size();i++){
j = i;
while(v[i].c==v[j].c&&j<v.size())j++;
sort(v.begin()+i,v.begin()+j,func2);
i=j;
}
for(i=0;i<k;i++){
m[make_pair(v[i].x,v[i].y)]=v[i].b;
}
it = m.begin();
for(;it!=m.end();++it){
u = it->first.first;
V = it->first.second;
w = it->second;
G[u].push_back(pii(V, w));
G[V].push_back(pii(u, w));
}
res = 0;
for(i=1;i<=n;i++){
dijkstra(i);
for(j= 1; j <= n; j++) {
if(i == j) continue;
if(d[j] >= INF) ;
else res+=d[j];
}
}
cout<<res/z<<"\n";
}
}
return 0;
}
It passes only 2 test cases out of 9 test cases . Why this algorithm didn't work ?
or What are the modification should be done in this algorithm to get accepted ?
Reference:
Rashof, Mayor of EMLand
Traverse the tree/graph (eg nonrecursive DFS starting from any node) and count the number of times each edge is used (number of nodes on one side * number of nodes on the other side)
For each possible rebuild multiply delta by count
Sort
Profit
Notice that this is a tree, so, each edge connects two connected components.
Assume that we have edge connect between two connected components A and B, which contains n and m numbers of intersections, so, by decreasing the edge by x unit, we will decrease the total distance by n*m*x.
A---B---C----E
| |
| |
D---- -----F
Look at the graph above, edge between B and C connect two connected components, (A,B,D) and (C,E,F), decreasing the weight of this edge will decrease the distance between (A,B,D) and (C,E,F)
So, the algorithm is to select k edges, which has the largest n*m*x (if x is positive).

Finding Longest Increasing Sub Sequence in a round table of numbers

I was recently working on the following problem.
http://www.codechef.com/problems/D2
The Chef is planning a buffet for the DirectiPlex inauguration party, and everyone is invited. On their way in, each guest picks up a sheet of paper containing a random number (this number may be repeated). The guests then sit down on a round table with their friends.
The Chef now decides that he would like to play a game. He asks you to pick a random person from your table and have them read their number out loud. Then, moving clockwise around the table, each person will read out their number. The goal is to find that set of numbers which forms an increasing subsequence. All people owning these numbers will be eligible for a lucky draw! One of the software developers is very excited about this prospect, and wants to maximize the number of people who are eligible for the lucky draw. So, he decides to write a program that decides who should read their number first so as to maximize the number of people that are eligible for the lucky draw. Can you beat him to it?
Input
The first line contains t, the number of test cases (about 15). Then t test cases follow. Each test case consists of two lines:
The first line contains a number N, the number of guests invited to the party.
The second line contains N numbers a1, a2, ..., an separated by spaces, which are the numbers written on the sheets of paper in clockwise order.
Output
For each test case, print a line containing a single number which is the maximum number of guests that can be eligible for participating the the lucky draw.
Constraints
1 ≤ N ≤ 10000
You may assume that each number number on the sheet of paper; ai is randomly generated, i.e. can be with equal probability any number from an interval [0,U], where U is some upper bound (1 ≤ U ≤ 106).
Example
Input:
3
2
0 0
3
3 2 1
6
4 8 6 1 5 2
Output:
1
2
4
On checking the solutions I found this code:
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <algorithm>
#define LIMIT 37
using namespace std;
struct node {
int val;
int index;
};
int N;
int binary(int number, vector<int>& ans) {
int start = 0;
int n = ans.size();
int end = n - 1;
int mid;
if (start == end)
return 0;
while (start != end) {
mid = (start + end) / 2;
if (ans[mid] == number)
break;
if (ans[mid] > number)
end = mid;
else
start = mid + 1;
}
mid = (start + end) / 2;
return mid;
}
void display(vector<int>& list) {
cout << endl;
for (int i = 0; i < list.size(); i++)
cout << list[i] << " ";
cout << endl;
}
int maxsubsequence(vector<int>& list) {
vector<int> ans;
int N = list.size();
ans.push_back(list[0]);
int i;
// display(list);
for (i = 1; i < N; i++) {
int index = binary(list[i], ans);
/*if(index+1<ans.size())
continue;*/
if (list[i] < ans[index])
ans[index] = list[i];
if (list[i] > ans[index])
ans.push_back(list[i]);
// display(ans);
}
return ans.size();
}
int compute(int index, int* g) {
vector<int> list;
list.push_back(g[index]);
int itr = (index + 1) % N;
while (itr != index) {
list.push_back(g[itr]);
itr = (itr + 1) % N;
}
return maxsubsequence(list);
}
int solve(int* g, vector<node> list) {
int i;
int ret = 1;
for (i = 0; i < min(LIMIT, (int)list.size()); i++) {
// cout<<list[i].index<<endl;
ret = max(ret, compute(list[i].index, g));
}
return ret;
}
bool cmp(const node& o1, const node& o2)
{ return (o1.val < o2.val); }
int g[10001];
int main() {
int t;
cin >> t;
while (t--) {
cin >> N;
vector<node> list;
int i;
for (i = 0; i < N; i++) {
node temp;
cin >> g[i];
temp.val = g[i];
temp.index = i;
list.push_back(temp);
}
sort(list.begin(), list.end(), cmp);
cout << solve(g, list) << endl;
}
return 0;
}
Can someone explain this to me. I am well aware of calculating LIS in nlog(n).
What I am not able to understand is this part:
int ret = 1;
for (i = 0; i < min(LIMIT, (int)list.size()); i++) {
// cout<<list[i].index<<endl;
ret = max(ret, compute(list[i].index, g));
}
and the reason behind sorting
sort(list.begin(),list.end(),cmp);
This algorithm is simply guessing at the starting point and computing the LIS for each of these guesses.
The first value in a LIS is likely to be a small number, so this algorithm simply tries the LIMIT smallest values as potential starting points.
The sort function is used to identify the smallest values.
The for loop is used to check each starting point in turn.
WARNING
Note that this algorithm may fail for certain inputs. For example, consider the sequence
0,1,2,..,49,9900,9901,...,99999,50,51,52,...,9899
The algorithm will try just the first 37 starting points and miss the best starting point at 50.
You can test this by changing the code to:
int main() {
int t;
t=1;
while (t--) {
N=10000;
vector<node> list;
int i;
for (i = 0; i < N; i++) {
node temp;
if (i<50)
g[i]=i;
else if (i<150)
g[i]=9999-150+i;
else
g[i]=i-100;
temp.val = g[i];
temp.index = i;
list.push_back(temp);
}
sort(list.begin(), list.end(), cmp);
cout << solve(g, list) << endl;
}
return 0;
}
This will generate different answers depending on whether LIMIT is 37 or 370.
In practice, for randomly generated sequences it will have a good chance of working (although I don't know how to compute the probability exactly).

Wrong search in A* pathing code

I have some C++ code I wrote to find an A* path, but it's behaving strangely. There's quite a bit of code here, so I'll split it into chunks and try to explain what I'm doing. I'm not gonna explain how A* pathing works. I assume if you're trying to help you already know the algorithm.
First off, here's my function for calculating the h value of a node:
int
calculateH(int cX, int cY, int eX, int eY, int horiCost = 10, int diagCost = 14) {
int h;
int xDist = abs(eX - cX);
int yDist = abs(eY - cY);
if (xDist > yDist)
h = (diagCost * yDist) + (horiCost * (xDist - yDist));
else
h = (diagCost * xDist) + (horiCost * (yDist - xDist));
return h;
}
I'm pretty sure there's no problem here; pretty simple stuff.
Next my Node class. And I know, I know, make those variables private and use getters; I just did it this way for testing purposes.
class Node {
public:
Node(int x_, int y_, int g_, int h_, int pX_, int pY_, bool list_) {
x = x_;
y = y_;
g = g_;
h = h_;
pX = pX_;
pY = pY_;
list = list_;
};
int x, y, g, h, pX, pY;
bool list;
};
Each Node has an X and Y variable. I only store G and H, not F, and calculate F when I need it (which is only once in my code). Then there's the Parent X and Y values. List is a boolean: fale = open list, true = closed list.
I also have a Object class. The only variables that matter here are X, Y, and Passable, all accessed through getters.
Now here's the start of my actual pathfinding code. It returns a string of numbers corresponding to directions as shown below:
432
501
678
So 1 means move right, 8 means go down and right, 0 means don't go anywhere.
string
findPath(int startX, int startY, int finishX, int finishY) {
// Check if we're already there.
if (startX == finishX && startY == finishY)
return "0";
// Check if the space is occupied.
for (int k = 0; k < objects.size(); k ++)
if ((objects[k] -> getX() == finishX) &&
(objects[k] -> getY() == finishY) &&
(!objects[k] -> canPass()))
return "0";
// The string that contains our path.
string path = "";
// The identifier of the current node.
int currentNode = 0;
// The list of nodes.
vector<Node> nodes;
// Add the starting node to the closed list.
nodes.push_back(Node(startX, startY, 0,
calculateH(startX, startY, finishX, finishY),
startX, startY, true));
Now we loop through until we find the destination. Note that sizeLimit is just to make sure we don't loop forever (it WONT if I can fix this code. As of right now it's very necessary). Everything from this point on, until I mark otherwise, is inside the i j loops.
int sizeLimit = 0;
while ((nodes[currentNode].x != finishX) | (nodes[currentNode].y != finishY)) {
// Check the surrounding spaces.
for (int i = -1; i <= 1; i ++) {
for (int j = -1; j <= 1; j ++) {
bool isEmpty = true;
// Check if there's a wall there.
for (int k = 0; k < objects.size(); k ++) {
if ((objects[k] -> getX() == (nodes[currentNode].x + i)) &&
(objects[k] -> getY() == (nodes[currentNode].y + j)) &&
(!objects[k] -> canPass())) {
isEmpty = false;
}
}
Next part:
// Check if it's on the closed list.
for (int k = 0; k < nodes.size(); k ++) {
if ((nodes[k].x == (nodes[currentNode].x + i)) &&
(nodes[k].y == (nodes[currentNode].y + j)) &&
(nodes[k].list)) {
isEmpty = false;
}
}
Continuing on:
// Check if it's on the open list.
for (int k = 0; k < nodes.size(); k ++) {
if ((nodes[k].x == (nodes[currentNode].x + i)) &&
(nodes[k].y == (nodes[currentNode].y + j)) &&
(!nodes[k].list)) {
// Check if the G score is lower from here.
if (nodes[currentNode].g + 10 + (abs(i * j) * 4) <= nodes[k].g) {
nodes[k].g = nodes[currentNode].g + 10 + (abs(i * j) * 4);
nodes[k].pX = nodes[currentNode].x;
nodes[k].pY = nodes[currentNode].y;
}
isEmpty = false;
}
}
This is the last part of the i j loop:
if (isEmpty) {
nodes.push_back(Node(nodes[currentNode].x + i,
nodes[currentNode].y + j,
nodes[currentNode].g + 10 + (abs(i * j) * 4),
calculateH(nodes[currentNode].x + i, nodes[currentNode].y + j, finishX, finishY),
nodes[currentNode].x,
nodes[currentNode].y,
false));
}
}
}
Now we find the Node with the lowest F score, change it to the current node, and add it to the closed list. The protection against infinite lopping is also finished up here:
// Set the current node to the one with the lowest F score.
int lowestF = (nodes[currentNode].g + nodes[currentNode].h);
int lowestFIndex = currentNode;
for (int k = 0; k < nodes.size(); k ++) {
if (((nodes[k].g + nodes[k].h) <= lowestF) &&
(!nodes[k].list)) {
lowestF = (nodes[k].g + nodes[k].h);
lowestFIndex = k;
}
}
currentNode = lowestFIndex;
// Change it to the closed list.
nodes[currentNode].list = true;
sizeLimit ++;
if (sizeLimit > 1000)
return "";
}
The problem I'm having is that it wont find certain paths. It seems to never work if the path goes up or left at any point. Down, left, and right all work fine. Most of the time anyway. I have absolutely no idea what's causing this problem; at one point I even tried manually following my code to see where the problem was. No surprise that didn't work.
And one more thing: if you're counting my curly braces (first of all wow, you have more dedication than I thought), you'll notice I'm missing a close brace at the end. Not to mention my return statement. There's a little bit of code at the end to actually make the path that I've left out. I know that that part's not the problem however; I currently have it commented out and it still doesn't work in the same way. I added some code to tell me where it's not working, and it's at the pathfinding part, not the interpretation.
Sorry my code's so messy and inefficient. I'm new to c++, so any critical advice on my technique is welcome as well.
I think that when you are looking for the next "currentNode", you should not start with lowestF = (nodes[currentNode].g + nodes[currentNode].h); because that value, in principle, will (always) be lower-or-equal-to any other nodes in the open-set. Just start with the value of std::numeric_limits<int>::max() or some very large number, or use a priority queue instead of an std::vector to store the nodes (like std::priority_queue, or boost::d_ary_heap_indirect).
I'm pretty sure that is the problem. And since your heuristic can very often be equal to the actual path-distance obtained, there is a good chance that following nodes in the open-set turn out to have the same cost (g+h) as the currentNode. This would explain why certain paths get explored and others don't and why it gets stuck.