Stuck with DFS/BFS task (USACO silver) - algorithm

competitive programming noob here. I've been trying to solve this question:
http://www.usaco.org/index.php?page=viewproblem2&cpid=646
The code I wrote only works with the first test case, and gives a Memory Limit Exceed error -- or ('!') for the rest of the test cases.
This is my code (accidently mixed up M and N):
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
using std::vector;
vector<int> check;
vector< vector<int> > A;
void dfs(int node)
{
check[node] = 1;
int siz = A[node].size();
for (int i = 0; i < siz; i++)
{
int y = A[node][i];
if (check[y] == 0)
{
dfs(y);
}
}
}
bool connected(vector<int> C)
{
for (int i = 1; i <= C.size() - 1; i++)
{
if (C[i] == 0)
{
return false;
}
}
return true;
}
int main()
{
freopen("closing.in", "r", stdin);
freopen("closing.out", "w", stdout);
ios_base::sync_with_stdio(false);
int M, N;
cin >> M >> N;
check.resize(M + 1);
A.resize(M + 1);
for (int i = 0; i < N; i++)
{
int u, v;
cin >> u >> v;
A[u].push_back(v); A[v].push_back(u);
}
dfs(1);
if (!connected(check)) {
cout << "NO" << "\n";
}
else {
cout << "YES" << "\n";
}
fill(check.begin(), check.end(), 0);
for (int j = 1; j < M; j++)
{
int node;
bool con = true;
cin >> node;
check[node] = -1;
for (int x = 1; x <= N; x++)
{
if (check[x] == 0)
{
dfs(x);
break;
}
}
if (!connected(check)) {
cout << "NO" << "\n";
}
else {
cout << "YES" << "\n";
}
for (int g = 1; g <= M; g++)
{
if (check[g] == 1)
{
check[g] = 0;
}
}
}
return 0;
}
basically,
void dfs(int node) searches through the bidirectional graph starting from node until it reaches a dead end, and for each node that is visited, check[node] will become 1.
(if visited -> 1, not visited -> 0, turned off -> -1).
bool connected(vector C) will take the check vector and see if there are any nodes that weren't visited. if this function returns true, it means that the graph is connected, and false if otherwise.
In the main function,
1) I save the bidirectional graph given in the task as an Adjacency list.
2) dfs through it first to see if the graph is initially connected (then print "Yes" or "NO") then reset check
3) from 1 to M, I take the input value of which barn would be closed, check[the input value] = -1, and dfs through it. After that, I reset the check vector, but keeping the -1 values so that those barns would be unavailable for the next loops of dfs.
I guess my algorithm makes sense, but why would this give an MLE, and how could I improve my solution? I really can't figure out why my code is giving MLEs.
Thanks so much!

Your DFS is taking huge load of stacks and thus causing MLE
Try to implement it with BFS which uses queue. Try to keep the queue as global rather than local.
Your approach will give you Time Limit Exceeded verdict. Try to solve it more efficiently. Say O(n).

Related

Djikstra's Algorithm in C++

I am writing Djikstra's algorithm in C++, and am having trouble getting the code to print anything other than the path from A->A. I need to get it to find the path from A->X where X is any one of the nodes A-I.
Expected Output
Actual Output
#include <climits>
#include <fstream>
#include <iostream>
using namespace std;
const int V = 9;
// Function to print shortest path from source to j
// using parent array
void printPath(int parent[], int j) {
char str = j;
// Base Case : If j is source
if (parent[j] == -1) {
cout << str << " ";
return;
}
printPath(parent, parent[j]);
// printf("%d ", j);
cout << str << " ";
}
int miniDist(int distance[], bool Tset[]) // finding minimum distance
{
int minimum = INT_MAX, ind;
for (int k = 0; k < V; k++) {
if (Tset[k] == false && distance[k] <= minimum) {
minimum = distance[k];
ind = k;
}
}
return ind;
}
void DijkstraAlgo(int graph[V][V], char sourceNode,
char endNode) // adjacency matrix
{
int distance[V]; // array to calculate the minimum distance for each node
bool Tset[V]; // boolean array to mark visited and unvisited for each node
int parent[V]; // Parent array to store shortest path tree
// Value of parent[v] for a vertex v stores parent vertex of v
// in shortest path tree.
for (int k = 0; k < V; k++) {
distance[k] = INT_MAX;
Tset[k] = false;
}
parent[sourceNode] = -1; // Parent of root (or source vertex) is -1.
distance[sourceNode] = 0; // Source vertex distance is set 0
for (int k = 0; k < V; k++) {
int end = miniDist(distance, Tset);
Tset[end] = true;
for (int k = 0; k < V; k++) {
// updating the distance of neighbouring vertex
if (!Tset[k] && graph[end][k] && distance[end] != INT_MAX &&
distance[end] + graph[end][k] < distance[k]) {
distance[k] = distance[end] + graph[end][k];
parent[k] = end;
}
}
}
cout << "Vertex\t\tDistance from source vertex\t\t Path" << endl;
int k = sourceNode;
char str = k;
// cout<<str<<"\t\t\t"<<distance[k]<<endl;
cout << str << "\t\t\t" << distance[k] << "\t\t\t\t\t\t\t\t";
printPath(parent, k);
cout << endl;
}
int main() {
char choice1, choice2;
ifstream inFile;
inFile.open("graph.txt");
int graph[V][V];
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++)
inFile >> graph[i][j];
}
cout << "Nodes: "
<< "A, B, C, D, E, F, G, H, I" << endl;
cout << "Enter starting node: ";
cin >> choice1;
cout << "Enter ending node: ";
cin >> choice2;
cout << endl;
DijkstraAlgo(graph, choice1, choice2);
inFile.close();
return 0;
}
So, I have to be able to pass the endNode parameter to something somehow, correct?
The main issue lies here:
int miniDist(int distance[], bool Tset[])
In your minimum distance calculation, you don't exclude the source vertex. All other vertices have INT_MAX distance and the source has 0 distance, so it gets picked. Try going through the algorithm once again to fix your implementation.

Finding all possible unique combinations of numbers to reach a given sum

We have a list of numbers, let's say: [ 2, 3, 5 ]
and we have a targetSum, let's say: 8
Our goal, then, is to pick numbers from the list in such a way that the sum of the numbers would lead to targetSum
I'll explain my code first, I wrote a simple c++ code for the same, it uses recursion and backtracking ( without memoization ). It does the following:
We subdivide our original problem by reducing the targetSum by each number at each recursion
Visualizing this in the form of a tree is useful, we also keep track of what number's we have substracted so far, and we keep pushing and popping accordingly
Once we hit the base case of 0, meaning it's possible to create the sum, we make a note of the current numbers we have recursed
This process goes on until we have gone through all of the possibilities
Code:
#include<iostream>
#include<vector>
using namespace std;
bool bestSum( int targetSum, vector<int> &holder, vector<vector<int>> &combinations,
vector<int> &path )
{
if( targetSum == 0 )
{
combinations.push_back( path );
return true;
}
if( targetSum < 0 )
{
return false;
}
bool possible = false;
for( int i = 0; i < holder.size(); i++ )
{
int remainder = targetSum - holder[i];
path.push_back(holder[i]);
cout << "After pushing:";
for( int j = 0; j < path.size(); j++ )
{
cout << path[j] << " ";
}
cout << endl;
bool verdict = bestSum( remainder, holder, combinations, path );
if( verdict == true )
{
possible = true;
}
path.pop_back();
cout << "After popping:";
for( int j = 0; j < path.size(); j++ )
{
cout << path[j] << " ";
}
cout << endl;
}
return possible;
}
int main()
{
vector<int> holder = { 2, 3, 5 };
int targetSum = 8;
vector<vector<int>> combinations;
vector<int> path;
bool verdict = bestSum( targetSum, holder, combinations, path );
for( int i = 0; i < combinations.size(); i++ )
{
for( int j = 0; j < combinations[i].size();j++)
{
cout << combinations[i][j] << " ";
}
cout << endl;
}
return 0;
}
(ignoring the printing statements) Talking about time complexity, it should be: exponential, without memoization
And at most small degree polynomial, with memoization
Combing back to the original problem, currently my code produces all of the possible combinations, for example, with the numbers list and targetSum presented at the start of this article, we would get: 2,3,3 and 3,3,2 as two different combinations. But we know that they aren't unique
My question is, is it possible to find all unique combination of numbers whilst keeping the logic of my code consistent?

Why Dijkstra + priority_queue performs worst compared to Dijkstra + queue?

I solved this problem using Dijkstra with a normal queue instead of a priority queue because interestingly I was getting a TLE when I used the priority queue. My understanding is that Dijkstra with priority queue should perform better. I tried analyzing as to why is this the case?
I came up with a thought that it is so because the question uses test cases with graphs having edges from 1 to n in increasing order of their weights so it is a waste of time to maintain a priority queue and hence the TLE. I would like to know if this is exactly the case an I'm not missing anything. Here is my solution to the problem:
#include <bits/stdc++.h>
using namespace std;
#define INF 100000000
queue<pair<long, int>> pi;
vector<long> dist;
map<int, vector<pair<int, long>>> mp;
int main() {
int m = 0, n = 0;
cin >> m >> n;
while (n--) {
int l = 0, k = 0, j = 0;
cin >> l >> k >> j;
mp[l].push_back(make_pair(k, j));
mp[k].push_back(make_pair(l, j)); // for undirected edge
}
dist.assign(m + 1, INF);
dist[1] = 0;
pi.push(make_pair(0, 1));
while (!pi.empty()) {
pair<int, int> p = pi.front(); pi.pop();
int u = p.second; long w = p.first;
if (w > dist[u]) continue;
for (auto e : mp[u]) {
if (max(dist[u], e.second) < dist[e.first]) {
dist[e.first] = max(dist[u], e.second);
if (e.first != m)
pi.push(make_pair(dist[e.first], e.first));
}
}
}
if (dist[m] == INF) cout << "NO PATH EXISTS" << endl;
else cout << dist[m] << endl;
return 0;
}
Did you change a default comparator of priority queue?
If not, then, of course, it would be slower
How can I create Min stl priority_queue?

Why is the merge printing in reverse order

So, I was learning the Merge Sort Algorithm. and I was amazed to see that the merging is printed in reverse order. You can see I am printing the merge vector v at each step but I don't understand why is it in reverse order. The final answer if perfectly fine.
void merge(vector<int> &left, vector<int> &right, vector<int> &v) {
cout << "merged vector is : \n";
for (auto x : v)
cout << x << " ";
cout << endl;
int l = left.size();
int r = right.size();
int i = 0, j = 0, k = 0;
while (i < l && j < r) {
if (left[i] <= right[j]) {
v[k] = left[i];
i++;
} else {
v[k] = right[j];
j++;
}
k++;
}
while (i < l) {
v[k++] = left[i++];
}
while (j < r) {
v[k++] = right[j++];
}
return;
}
You print the destination vector v at the beginning of each step. The contents and order of the destination vector depends on how you use implement the merge sort algorithm, namely how you split the original vector, how you invoke the merge function and what is the original contents of the source vector. If you want to track the behavior of the merge sort algorithm, you should print the vector after the merge operation.
Note also that:
the index variables i, j, k, l and r should have type size_t.
the return; statement at the end of the function is useless.

Codechef GALACTIK solution

Here's the link for the problem:
I have used union-find algorithm to solve the problem.
Code:
#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
#define INT 100000000
unordered_map<ll, ll> parent;
unordered_map<ll, ll> depth;
std::vector<ll> cost;
ll find_set(ll x) {
if (x == parent[x])return x;
parent[x] = find_set(parent[x]);
return parent[x];
}
void union_set(ll x, ll y) {
/*
Creating a disjoint set such that the node with smallest cost
being the root using union-rank concept.
*/
ll rep1 = find_set(x), rep2 = find_set(y);
if (depth[rep1] > depth[rep2])parent[rep1] = rep2;
else if (depth[rep2] >= depth[rep1])parent[rep2] = rep1;
}
int main() {
ll n, m;
cin >> n >> m;
ll c[m + 1][3];
for (ll i = 1; i <= m; i++) {
cin >> c[i][1] >> c[i][2]; //Accepting the edges
}
for (ll i = 1; i <= n; i++) {
parent[i] = i;
cin >> depth[i];
if (depth[i] < 0)depth[i] = INT;
/*we assume that each negative cost is replaced by a very
large positive cost.*/
}
for (ll i = 1; i <= m; i++) {
union_set(c[i][1], c[i][2]);
}
set<ll> s;
std::vector<ll> v;
//storing representatives of each connected component
for (auto i = 1; i <= n; i++)s.insert(depth[find_set(i)]);
for (auto it = s.begin(); it != s.end(); it++)v.push_back(*it);
sort(v.begin(), v.end());
if (s.size() == 1) {
//Graph is connected if there is only 1 connected comp
cout << 0 << endl;
return 0;
}
bool flag = false;
ll p = 0;
for (ll i = 1; i < v.size(); i++) {
if (v[i] == INT) {
flag = true;
break;
}
p += (v[0]+v[i]);
}
if (flag)cout << -1 << endl;
else cout << p << endl;
return 0;
}
Logic used in my program:
To find the answer, take the minimum value of all the valid values in a connected component.Now to make the graph connected, Take the minimum value of all the values we got from the above step and make edge from that node to all the remaining nodes.If graph is already connected than answer is 0.if there exists a connected component where all nodes are not valid to be chosen, than answer is not possible (-1).
But this solution is not accepted?What's wrong with it?

Resources