Hello
I was trying to solve problem H in this gym contest , the problem shortly states:
given a connected graph you are required to add one edge so that the number of bridges in the graph is minimized, an edge is called bridge if and only if removing it makes the graph disconnected.
the solution is very simple , find bridges in O(n+m) then change every connected component of non-bridge edges into one node , then you will have a tree consisting of bridge edges only , then find the length of diameter of tree and subtract it from the number of bridges and this is the final answer
this algorithm is O(n+m) time and memory but when I coded it , it gave me Memory limit exceeded which is very strange since all arrays which I allocated can't be more than 256MB , here's my code which took MLE.
then I tried to code it without using structs or pointers but I used vectors, and it got AC with only 16MB memory , link to my AC code , as you can see there's big difference in memory and I have no idea what is happening.
I wrote a code that calculate the sum of number of nodes and the number of edges in all test cases of input file and the result was: sum of nodes=639884 , sum of edges=678426
any help explaining the MLE would be appreciated.
I observed the same problem when trying to index strings using trie vs map. Although trie is faster yet it takes a lot of memory and it ends up usually with MLE and I have no idea why.
note that memory complexity of trie is O(a*s) where
s
is sum of strings lengths anda
is alphabet size which is 26 in case of only lowercase letters are allowed, map doesn't have the alphabet factor in its memory complexity.this might be the reason in your case
First, you have provided misleading code — it differs from your only submission which gets ML in the system. I guess it's not a big deal.Wrong statement was here, my bad, I'm sorryThe main problem is that you do not understand purpose of C++ destructors. You should never call them manually, unless you're writing your own data structure which pre-allocates memory. When you create
vector
(or any other well-implemented C++ class) you are typically allowed to do any operations with it (including assignments, copying and so on) without any memory leaks as long as you don't create raw pointers. So, instead of:you should write:
or
In either case the vector will be cleared to all pre-allocated memory will be de-allocated.
What happened before is what's called double free: memory is deallocated by explicitly called destructor first and then it's deallocated inside
operator=
. Double free is undefined behavior, possibly causing damage to memory allocator and, therefore, any kind of special effects.After I've replaced these lines your code received runtime error, which I think can be reproduced locally.
Thanks yeputons for replying
no it is the same code which gave me MLE , you can resubmit it to see MLE
I did that because I have read that
clear
method ofvector
doesn't destruct the struct of array elements (which is in my caseedge
) , I have already tried to call onlyclear
andshrink_to_fit
but I got the same MLE verdict , but yours got RTE because I temporarily changed the memory limit to 1024 MB instead of 256 MB and notice that your submission also have more than 256 MB memory.I guess I knew what is the problem I will write a comment explaining what happened soon
There can be different ways of getting MLE. The submission I'm talking about is 12031262. E.g. there are differences in lineMy bad, irrelevant to the main question.int mx=-1,smx=-1;
and the submission also adds some extra wrong vector clearing.Where have you read that? It's wrong.
vector
calls destructor of underlying elements.I have a lot of submissions with verdict
MLE
to this problem , each one slightly differ from others , I picked one of them and pasted it to blog but you went to my submissions and opened another one to see there's actually those differences that's what happened.and about the wrong information , I have read it on the internet but I don't remember where exactly because that was long time ago
Oh, my bad. I'm really sorry.
it is true only for pointers. For example, here you must call destructors before clearing vector to prevent memory leak:
(of cource in real code you should use shared pointers or whatever):
it seems that I forgot to reset the variable
is
tofalse
of array (which is telling whether an edge is bridge or not) in the start of every test case. when I added extra linelist[i].is=false
the memory reduced to about 10 MB , but it is still not clear why this bug caused MLE, I tried to see what happens if non-bridge edges are considered as bridge and it seems that it causes the tree to have self-loop edges (an edge that connects a node with itself) and the self-loops causes infinite loop in nested callingdfs3
function which I think caused stack overflow.to make sure I have tried to execute this code in custom test that is supposed to cause stack overflow
surprisingly it's giving TLE with very low memory consumption , anyone can explain it?
so I think there must be another reason why forgetting it reset bridges causing MLE.
moral of story, bugs don't only cause WA, it can cause TLE , RTE and MLE too
My suggestion is that it's caused by tail call optimization — optimizer can replace recursion with a loop in your code: you do nothing after the only recursive call and there is no reason to preserve local variables.
This one successfully crashes both on my Windows laptop and in gym:
I thought that it might be because of some compiler optimization but I'm not experienced with them so I couldn't know what exactly the optimization is doing , so I tried to make array
g
and using random cell to stop the optimization but it seems it was not enough. thanks for information :)