Counting number of members in a disjoint set - c++

I am having a little bit of trouble counting the number of elements in each of my disjoint set members. For example, if someone enters in:
Note: first number = source vertex, 2nd number = destination vertex, 3rd number = length
0 2 1
4 8 7
5 8 6
1 2 5
2 3 17
I would have 2 as a count for the set
4 8 7
5 8 6
and 3 as a count for the set as both are connected by 2 and 3 (respective) elements.
0 2 1
1 2 5
2 3 17
I had the idea of storing the count of the number of elements for each disjoint set, into an integer array, so I can access the count for ever disjoint set. Below are my implementations for finding elements and unioning them into the same set. I also have a function for finding the root in every set.
int node::findSet(int v, int *parent)
{
if(parent[v] < 0)
return v;
else
{
return parent[v] = findSet(parent[v], parent);
}
}
void node::joinSets(int c, int p1, int p2, int *parents)
{
join(findSet(p1,parents),findSet(p2,parents),parents);
}
void node::join(int p1, int p2, int *parents)
{
if(parents[p2] < parents[p1])
parents[p1] = p2;
else
{
if(parents[p1] == parents[p2])
parents[p1]--;
parents[p2] = p1;
}
}
I'm just not sure where/when to increment and maintain my counter. Any help would be appreciated. Thanks!

If you want to count the number of edges connecting each disjoint set, store the current size of every root in an array similar to parents.
When an edge comes, find roots of both nodes. If they're equal, increment the counter for the root (I'm assuming there are no repeating edges). If they're not, union the roots, and for the resultant root's counter value put the sum of the roots' counter values plus one.

Related

Erasing duplicates from nested map (k-core algorithm)

i am trying to finish a homework and came with an error.
What i am trying to do is: There is an unordered map with keys containing values as unordered_set which may have keys inside that "may have the same names as keys of unordered outer map".
To be spesific, i am trying to implement a k-core algorithm, the count and k numbers are there for it. I am trying to erase unordered_map keys (lets call P) that have unordered_sets(containing neighbors, call N) which have lower than k sizes.
But since i delete the N entry from the P node, i also need to find the N entry from the outer map and find P and delete it there.
The following code is what i came up with so far (there is more but i don't want plagiarism or anything). During the debugging i noticed that somewhat cout<<nodes.size() never executes. I don't know what is the problem. Probably the inner while infinitely loops, and it is related with how iterators function with erase etc. but i am not sure. Hope it is clear.
EDIT : I have updated the code according to the suggestion, but there is a new problem: for k=1 the amount of nodes are right. But after k=2,3,4.. there is infinite loop of outermost loop. And the size stays constant (after taking out nodes with k=1)
while(nodes.size()!=0)
{
int count = 0;
while(stillLeft){
stillLeft = 0;
it = nodes.begin();
while(it != nodes.end()){
if(it->second.size()<=k){
stillLeft = 1;
++count;
for ( innerit = it->second.begin(); innerit != it->second.end();innerit++ ){
nodes.find(*innerit)->second.erase(it->first);
}
it = nodes.erase(it);
}
else{
++it;
}
}
}
cout << nodes.size() << endl;
kCoreNumbers.push_back(count);
++k;
}
You don't need to loop till while(nodes.size()!=0) i.e. till nodes is empty. After running the k-core algorithm, you might be left with nodes whose vertexes have a degree greater than k. You only need to loop till there are no more nodes that have a degree less than k.
Your initial code was in the right direction but with the problem of deleting a node in the map while traversing it. I solved it by just adding the vertices to be deleted to a vector while iterating the graph and then after each graph traversal just deleted all the nodes makred for deletion. Here's my version of code, based off yours with a few variable name changes to reflect what they are:
void runKCore(size_t k) {
bool nodesDeleted;
do {
// To keep track of how many times the loop needs to run
nodesDeleted = false;
// To keep track of the nodes to be deleted after one pass
std::vector<int> markDeleted;
for(auto const& node: graph) {
auto vertex = node.first;
auto const& neighbours = node.second;
// Pick vertex if it has less than k neighbours
if(neighbours.size() < k) {
for(auto neighbour: neighbours) {
// Get the neighbour's list of vertices
auto& neighBoursEdges = graph.at(neighbour);
// Remove vertex from this list
neighBoursEdges.erase(vertex);
}
// Mark this node as deleted
markDeleted.push_back(vertex);
}
}
// Update nodesDeleted if any nodes were marked for deletion
nodesDeleted = markDeleted.size() > 0;
for(auto node: markDeleted) {
// Erase the node and it's neighbours
graph.erase(node);
}
}while(nodesDeleted);
}
Here is the complete program.
For an initial graph
translating to initial graph
8: 6 5
7: 6 3 4
6: 7 3 8 5 4 2
5: 8 6 2 1
4: 7 3 6 2
3: 7 6 4 2
2: 6 3 4 5 1 0
1: 5 2 0
0: 2 1
the output of the program after running the k-core algo for k = 3 output is:
7: 6 3 4
6: 7 3 4 2
4: 7 3 6 2
3: 7 6 4 2
2: 6 3 4

To make array identical by swapping elements

There are 2 i/p array's. They are identical when they have exactly same numbers in it. To make them identical, we can swap their elements. Swapping will have cost. If we are swapping a and b elements then cost = min(a, b).
While making array's identical, cost should be minimum.
If it is not possible to make array identical then print -1.
i/p:
3 6 6 2
2 7 7 3
o/p :
4
Here I have swapped (2,7) and (2,6). So min Cost = 2 + 2 = 4.
Logic :
Make 2 maps which will store frequency of i/p array's elements.
if element "a" in aMap is also present in bMap, then we have to consider number of swapping for a = abs(freq(a) in aMap - freq(a) in bMap)
if frequency of elements is "odd", then not possible to make them identical.
else , add total swaps from both maps and find cost using
cost = smallest element * total swaps
Here is the code
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
int main()
{
int t;
cin >> t;
while(t--)
{
int size;
long long int cost = 0;
cin >> size;
bool flag = false;
map<long long int, int> aMap;
map<long long int, int> bMap;
// storing frequency of elements of 1st input array in map
for( int i = 0 ; i < size; i++)
{
long long int no;
cin >> no;
aMap[no]++;
}
// storing frequency of elements of 2nd input array in map
for(int i = 0 ; i < size; i++)
{
long long int no;
cin >> no;
bMap[no]++;
}
// fetching smallest element (i.e. 1st element) from both map
long long int firstNo = aMap.begin()->first;
long long int secondNo = bMap.begin()->first;
long long int smallestNo;
// finding smallest element from both maps
if(firstNo < secondNo)
smallestNo = firstNo;
else
smallestNo = secondNo;
map<long long int, int> :: iterator itr;
// trying to find out total number of swaps we have to perform
int totalSwapsFromA = 0;
int totalSwapsFromB = 0;
// trversing a map
for(itr = aMap.begin(); itr != aMap.end(); itr++)
{
// if element "a" in aMap is also present in bMap, then we have to consider
// number of swapping = abs(freq(a) in aMap - freq(a) in bMap)
auto newItr = bMap.find(itr->first);
if(newItr != bMap.end())
{
if(itr->second >= newItr->second)
{
itr->second -= newItr->second;
newItr->second = 0;
}
else
{
newItr->second -= itr->second;
itr->second = 0;
}
}
// if freq is "odd" then, this input is invalid as it can not be swapped
if(itr->second & 1 )
{
flag = true;
break;
}
else
{
// if freq is even, then we need to swap only for freq(a)/ 2 times
itr->second /= 2;
// if swapping element is smallest element then we required 1 less swap
if(itr->first == smallestNo && itr->second != 0)
totalSwapsFromA += itr->second -1;
else
totalSwapsFromA += itr->second;
}
}
// traversing bMap to check whether there any number is present which is
// not in aMap.
if(!flag)
{
for(itr = bMap.begin(); itr != bMap.end(); itr++)
{
auto newItr = aMap.find(itr->first);
if( newItr == aMap.end())
{
// if frew is odd , then i/p is invalid
if(itr->second & 1)
{
flag = true;
break;
}
else
{
itr->second /= 2;
// if swapping element is smallest element then we required 1 less swap
if(itr->first == smallestNo && itr->second != 0)
totalSwapsFromB += itr->second -1;
else
totalSwapsFromB += itr->second;
}
}
}
}
if( !flag )
{
cost = smallestNo * (totalSwapsFromB + totalSwapsFromA);
cout<<"cost "<<cost <<endl;
}
else
cout<<"-1"<<endl;
}
return 0;
}
No error in the above code but giving wrong answer and not getting accepted.
Can anyone improve this code / logic ?
Suppose you have 2 arrays:
A: 1 5 5
B: 1 4 4
We know that we want to move a 5 down and a 4 up, so we have to options: swapping 4 by 5 (with cost min(4, 5) = 4) or using the minimum element to do achive the same result, making 2 swaps:
A: 1 5 5 swap 1 by 4 (cost 1)
B: 1 4 4
________
A: 4 5 5 swap 1 by 5 (cost 1)
B: 1 1 4
________
A: 4 1 5 total cost: 2
B: 5 1 4
So the question we do at every swap is this. Is it better to swap directly or swapping twice using the minimum element as pivot?
In a nutshell, let m be the minimum element in both arrays and you want to swap i for j. The cost of the swap will be
min( min(i,j), 2 * m )
So just find out all the swaps you need to do, apply this formula and sum the results to get your answer.
#user1745866 You can simplify your task of determining the answer -1 by using only variable:
let we have int x=0 and we will just do XOR of all the i/p integers like this:
int x = 0;
for(int i=0;i<n;i++){
cin>>a[i];
x = x^a[i];
}
for(int i=0;i<n;i++){
cin>>b[i];
x = x^b[i];
}
if(x!=0)
cout<<-1;
else{
...do code for remain 2 condition...
}
Now the point is how it will work because , as all the numbers of both array should occurs only even number of times and when we do XOR operation of any number which occured even number of times we will get 0.... otherwise they can't be identical arrays.
Now for 2nd condition(which gives answer 0) you should use multimap so you would be able to directly compare both arrays in O(n) time complexity as if all elements of both arrays are same you can output:0
(Notice: i am suggesting multimap because 1:You would have both array sorted and all elements would be there means also duplicates.
2: because they are sorted, if they consist of same element at same position we can output:0 otherwise you have to proceed further for your 3rd condition or have to swap the elements.)
For reducing the swap cost see Daniel's answer. For finding if the swap is actually possible, please do the following, the swaps are actually only possible if you have an even number of elements in total, so that you can split them out evenly, so if you have 2, 4 or 6 5's you are good, but if you have 1, 3, or 5 5's return -1. It is impossible if your number of duplicates of a number is odd. For actually solving the problem, there is a very simple solution I can think of, through it is a little bit expensive, you just need to make sure that there are the same number of elements on each side so the simple way to do that would be to declare a new array:
int temp[size of original arrays];
//Go through both arrays and store them in temp
Take half of each element, so something like:
int count[max element in array - min element in array];
for(int i = 0; i < temp.size(); i++){
count[temp[i]]++;
}
Take half of each element from temp. When you see an element that matches a element on your count array so whenever you see a 1 decrement the index on the count array by 1, so something like count[1]--; Assuming count starts at 0. If the index is at zero and the element is that one, that means a swap needs to be done, in this case find the next min in the other array and swap them. Albeit a little bit expensive, but it is the simplest way I can think of. So for example in your case:
i/p:
3 6 6 2
2 7 7 3
o/p :
4
We would need to store the min index as 2. Cause that is the smallest one. So we would have an array that looks like the following:
1 1 0 0 1 1
//one two one three zero four zero five 1 six and 1 seven
You would go through the first array, when you see the second six, your array index at 6 would be zero, so you know you need to swap it, you would find the min in the other array, which is 2 and then swap 6 with 2, after wards you can go through the array smoothly. Finally you go through the second array, afterwards when you see the last 7 it will look for the min on the other side swap them...., which is two, note that if you had 3 twos on one side and one two on the other, chances are the three twos will go to the other side, and 2 of them will come back, because we are always swapping the min, so there will always be an even number of ways we can rearrange the elements.
Problem link https://www.codechef.com/JULY20B/problems/CHFNSWPS
here for calculating minimum number of swap.we will having 2 cases
let say an example
l1=[1,2,2]
l2=[1,5,5]
case 1. swap each pair wrt to min(l1,l2)=1
step 1 swapping single 2 of a pair of 2 from l1-> [1,1,2]
[2,5,5] cost is 1
step 2 swapping single 5 of a pair of 5 from l1-> [1,5,2]
[2,1,5] cost is 1
total cost is 2
case 2. swap min of l1 with max of l2(repeat until both list end)
try to think if we sort 1st list in increasing order and other as decreasing order then we can minimize cost.
l1=[1,2,2]
l2=[5,5,1]
Trick is that we only need to store min(l1,l2) in variable say mn. Then remove all common element from both list.
now list became l1=[2,2]
l2=[5,5]
then swap each element from index 0 to len(l1)-1 with jump of 2 like 0,2,4,6..... because each odd neighbour wiil be same as previous number.
after perform swapping cost will be 2 and
l1=[5,2]
l2=[2,5] cost is 2
total cost is 2
Let say an other example
l1=[2,2,5,5]
l2=[3,3,4,4]
after solving wrt to min(l1,l2) total cost will be 2+2+2=6
but cost after sorting list will be swap of ((2,4) and (5,3)) is 2+3=5
so minimum swap to make list identical is min(5,6)=5
//code
l1.sort()
l2.sort(reverse=True)
sums=0
for i in range(len(l1)):
sums+=min(min(l1[i],l2[i]),2*minimum))
print(sums)
#print -1 if u get odd count of a key in total (means sums of count of key in both list)

Populating a vector with numbers and conditions in C++

Working on a business class assignment where we're using Excel to solve a problem with the following setup and conditions, but I wanted to find solutions by writing some code in C++ which is what I'm most familiar from some school courses.
We have 4 stores where we need to invest 10 million dollars. The main conditions are:
It is necessary to invest at least 1mil per store.
The investments in the 4 stores must total 10 million.
Following the rules above, the most one can invest in a single store is 7 million
Each store has its own unique return of investment percentages based off the amount of money invested per store.
In other words, there is a large number of combinations that can be obtained by investing in each store. Repetition of numbers does not matter as long as the total is 10 per combination, but the order of the numbers does matter.
If my math is right, the total number of combinations is 7^4 = 2401, but the number of working solutions
is lesser due to the condition that each combination must equal 10 as a sum.
What I'm trying to do in C++ is use loops to populate each row with 4 numbers such that their sum equals 10 (millions), for example:
7 1 1 1
1 7 1 1
1 1 7 1
1 1 1 7
6 2 1 1
6 1 2 1
6 1 1 2
5 3 1 1
5 1 3 1
5 1 1 3
5 1 2 2
5 2 1 2
5 2 2 1
I'd appreciate advice on how to tackle this. Still not quite sure if using loops is a good idea whilst using an array (2D Array/Vector perhaps?) I've a vague idea that maybe recursive functions would facilitate a solution.
Thanks for taking some time to read, I appreciate any and all advice for coming up with solutions.
Edit:
Here's some code I worked on to just get 50 rows of numbers randomized. Still have to implement the conditions where valid row combinations must be the sum total of 10 between the 4;
int main(){
const int rows = 50;
int values[rows][4];
for (int i = 0; i < 50; i++) {
for (int j = 0; j <= 3; j++){
values[i][j]= (rand() % 7 + 1);
cout << values[i][j] << " ";
}
cout << endl;
}
}
You can calculate this recursively. For each level, you have:
A target sum
The number of elements in that level
The minimum value each individual element can have
First, we determine our return type. What's your final output? Looks like a vector of vectors to me. So our recursive function will return a the same.
Second, we determine the result of our degenerate case (at the "bottom" of the recursion), when the number of elements in this level is 1.
std::vector<std::vector<std::size_t>> recursive_combinations(std::size_t sum, std::size_t min_val, std::size_t num_elements)
{
std::vector<std::vector<std::size_t>> result {};
if (num_elements == 1)
{
result.push_back(std::vector<std::size_t>{sum});
return result;
}
...non-degenerate case goes here...
return result;
}
Next, we determine what happens when this level has more than 1 element in it. Split the sum into all possible pairs of the "first" element and the "remaining" group. e.g., if we have a target sum of 5, 3 num_elements, and a min_val of 1, we'd generate the pairs {1,4}, {2,3}, and {3,2}, where the first number in each pair is for the first element, and the second number in each pair is the remaining sum left over for the remaining group.
Recursively call the recursive_combinations function using this second number as the new sum, and num_elements - 1 as the new num_elements to find the vector of vectors for the remaining group, and for each vector in the return vector, append the first element from the above set.

Query based shifting elements in array

We are given two numbers n and m. n indicates the number of elements in the array and m indicates number of queries.We are given m queries.We need to perform two types of queries on the array.Queries can be of two types, type 1 and type 2.
TYPE 1 queries are represented as (1 i j ) : Modify the given array by removing elements between i to j position and adding them to the front.
TYPE 2 queries are represented as (2 i j ) : Modify the given array by removing elements between i to j position and adding them to the back.
Our task is to simply print the difference array[1]-array[n] after the execution of queries followed by printing the array.
INPUT FORMAT:
First line consists of two space-separated integers, n and m.
Second line contains n integers, which represent the elements of the array.
m queries follow. Each line contains a query of either type 1 or type 2 in the form (type i j).
OUTPUT FORMAT:
Print the absolute value a[0]-a[n] in the first line.
Print elements of the resulting array in the second line. Each element should be separated by a single space.
EXAMPLE:
Given array is [1,2,3,4,5,6,7,8].
After execution of query(1 2 4),the array becomes(2,3,4,1,5,6,7,8).
After execution of query(2 3 5),the array becomes(2,3,6,7,8,4,1,5).
After execution of query(1 4 7),the array becomes(7,8,4,1,2,3,6,5).
After execution of query(2 1 4),the array becomes(2,3,6,5,7,8,4,1).
For the problem,I wrote a program as follows:
int main()
{
int n,m;
cin>>n;
cin>>m;
int arr[n];
for(int i=0;i<n;i++)
{
cin>>arr[i];
}
int count; // counter to control no of queries to accept
for(count=0;count<m;count++)
{
int type,start,end; // 3 parts of query
cin>>type;cin>>start;cin>>end;
if(type==1)
{
//calculated difference between (start,end) to find no of iterations
for(int i=0;i<=(start-end);i++)
{ // t is temporary variable
int t=arr[(start-1)+i]; //(start-1) as index starts from 0
arr[(start-1)+i]=arr[i];
arr[i]=t;
}
}
else
{
for(int i=0;i<=(start-end);i++)
{
int t=arr[(start-1)+i];
// elements inserted from end so we subtract (n)-(start-end)
arr[(start-1)+i]=arr[(n-1)-(start-end)+i];
arr[(n-1)-(start-end)+i]=t;
}
}
count++;
//increment count
}
int absolute=abs(arr[0]-arr[n-1]);
cout<<absolute<<"\n";
for(int i=0;i<n;i++)
{
cout<<arr[i]<<" "<<endl;
}
return 0;
}
I was expecting the code to work correctly,but surprisngly did not even display the output correctly as well.Here is the test case:
INPUT:
8 4
1 2 3 4 5 6 7 8
1 2 4
2 3 5
1 4 7
2 1 4
EXPECTED OUTPUT:
1
2 3 6 5 7 8 4 1
MY OUTPUT:
7
1
2
3
4
5
6
7
8
I had dry run the code many times but cannot seem to understand where the problem is coming from.Please look at the code and provide me suggestions.
The for loop condition is wrong.
CORRECT WAY : for ( int i = 0; i<=(end - start ) ; i++)

Make QuickSort sort by multiple criteria?

Is there anyway to make a quicksort sort by multiple conditions? For example, I have a set of edges. Each edge has a source, destination, and length. I want to put the edge with a smaller length in my array first. But if the lengths are the same, I want to sort by that with a smaller source vertex. If these source vertexes are the same, I want to sort by the smaller of the two destination vertices.
For example:
4 (source) 2 (destination) 3 (length)
1 (source) 5 (destination) 3 (length)
Since they both have the same length, we look at the source vertex. Since the second edge is smaller than the first edge, we swap them because we compare by source vertex.
Below is my quicksort and I'm honestly not sure why it's not sorting correctly.If there's a way to make quicksort less efficient but more stable, I would gladly take suggestions!
void quickSort(edge *e, int left, int right)
{
int i = left, j = right;
int temp, temp1, temp2;
int pivot = (left + right)/2;
while(i <= j)
{
while(e[i] < e[pivot])
i++;
while(e[pivot] < e[j])
j--;
if(i <= j)
{
temp = e[i].getLength();
temp1 = e[i].getEdgeSrc();
temp2 = e[i].getEdgeDes();
e[i].setLength(e[j].getLength());
e[i].setEdgeSrc(e[j].getEdgeSrc());
e[i].setEdgeDes(e[j].getEdgeDes());
e[j].setLength(temp);
e[j].setEdgeSrc(temp1);
e[j].setEdgeDes(temp2);
i++;
j--;
} //if statement
}///while loop
if(left < j)
quickSort(e, left, j);
if(i < right)
quickSort(e, i, right);
}
My sorting of conditions:
bool edge::operator<(const edge &other) const
{
if (length < other.length)
return true;
else if ((length == other.length) && (source < other.source))
return true;
else if((length == other.length) && (source == other.source) && (destination < other.destination))
return true;
return false;
}
Again, if anyone knows a way to make this quicksort correctly by reducing the time complexity of it but making it stable, I would gladly take any suggestions! Thank you! Any help?
Edit: This is how I invoked my quicksort. I invoked it based on the number of edges read.
quickSort(e, 0, edges-1); //-1 because if you put in edges, it'd go past the bounds of the array
EDIT: when I try to put in something like this in my algorithm:
0 1 1
0 3 1
1 3 1
2 5 1
4 10 1
4 8 1
10 8 1
11 6 2
11 7 2
6 7 1
9 6 1
9 7 1
This is the output:
0 1 1
0 3 1
1 3 1
2 5 1
4 8 1
4 10 1
6 7 1
6 9 1
8 10 1 <- should be below 7 9 1
7 9 1 <- should be above 8 10 1
6 11 2
7 11 2
It is cleaner to write it this way
if (length != other.length)
return length<other.length;
if ( source != other.source)
return source < other.source;
return destination < other.destination;
You should also be able to do temp = e[i] and so on since the members are all ints.
This (and the code you submitted) should do the task you want I think.
If you are having stability issues, thats because quicksort isnt stable. You could get around it by adding more conditions so that lhs==rhs doesnt happen. Alternatively you can try Mergesort
I dont have much experience with Quick sort frankly, but your impl does look markedly different from Wikipedias In Place Algorithm. For instance, your pivot is not moved at all. Could you check if that is the problem?
Edit
After looking at your link
It looks like the algorithm linked also uses pivot as a value instead of as an index (as you do). It looks syntactically identical to yours until you consider that your pivot value might move, after which your pivot index would point to something else
int pivot = arr[(left + right) / 2];
Does this help?
EDIT: Here's pseudocode for in-place quicksort: http://en.wikipedia.org/wiki/Quicksort#In-place_version
This differs from your code in that the pivot is a value (an average of the left and right values) rather than an index.
If you're looking for a simple non-optimal solution, mergesort the entire list by destination vertex, then mergesort the entire list by origin vertex, then mergesort the entire list by edge length. This takes advantage of the fact that mergesort is a stable sort algorithm and has running time O(E) on the number of edges.