7
\$\begingroup\$

Recently I came across this problem ,

A despotic king decided that his kingdom needed to be rid of corruption and disparity. He called his prime minister and ordered that all corrupt citizens be put to death. Moreover, he wanted this done quickly.

The wily prime minister realised that investigating every citizen to decide who was corrupt and who was not was rather difficult. So he decided on the following plan: He ordered all the citizens to appear in the court one by one and declare their wealth.

The king does not sit in the court all the time (he has other important business to attend to - for instance, meet dignitaries from neighbouring kingdoms, spend time with his family ...) Whenever the king walks into the court, the prime minister pulls out the richest man who has appeared before the court so far and is still alive and beheads him for being corrupt. Since the rich are more likely to be corrupt, he hopes to get rid of most of the corrupt and the king is happy as he sees his policy being implemented enthusiastically.

Suppose the wealth of the citizens trooping into the court is 1 3 7 6 5 18 9 11 2 4

and the king walked in three times: the first time after the first four persons have seen the minister, the second time after the first five persons have seen the minister and, finally after the first nine persons have seen the minister.

At the king's first visit the richest person to have met the minister has wealth 7 and he would be beheaded. At the second visit, the wealth of the richest person who has met the minister and is still alive has wealth 6 and so he would be beheaded. At the third visit the richest person to have met the minister who is still alive has wealth 18 and so he would be beheaded.

You may assume that the input is such that whenever the king walks in, it is always possible to behead someone.

Your aim is to write a program that will enable the prime minister to identify the richest man to have met the minister and who is still alive quickly. You may assume that no two citizens have the same wealth.

Input format

The first line of the input consists of two numbers N and M, where N is the number of citizens in the kingdom and M is the number of visits to the court by the king.

The next N+M lines describe the order in which the N citizens' appearances are interleaved with the M visits by the king. A citizen's visit is denoted by a positive integer, signifying his wealth. You may assume that no two citizens have the same wealth. A visit by the king is denoted by -1.

Output format

Your output should consist of M lines, where the ith line contains the wealth of the citizen who is beheaded at the ith visit of the king.

Test Data:

You may assume that M ≤ 10000 and N ≤ 100000. You may further assume that in 50% of the inputs M ≤ 1000 and N ≤ 8000.

My approach is like that ,until the king approaches store the numbers in an array , when the king approaches get the max number out of array , print it and make it 0 , the lowest value , but this is showing a TLE(Time Limit exceeded) for the last 3 testcases.

Do anyone have any better approach to this problem?

Here is the code:

#include <iostream>
#include <vector>
#include <algorithm>

int findGreatest(std::vector<int>vec){
    int n = vec.size();
    int arr[n][n];
    for(int i=0;i<n;i++){
        arr[n][i] = vec[i];
    }
    for(int i=n-2;i>=0;i--){
        for(int j=0;j<n-1;j++){
            if(arr[i+1][j] > arr[i+1][j+1]){
                arr[i][j] = arr[i+1][j];
            }else{
                arr[i][j] = arr[i+1][j+1];
            }
        }
    }
    return arr[0][0];
}

int getPos(std::vector<int> vec, int num){
    for(int i=0;i < vec.size();i++){
        if(num == vec[i]){
            return i;
            break;
        }
    }
}


int main (int argc, char const* argv[])
{
    int n , k ,i=0;
    std::cin >> n >> k;
    std::vector<int>riches , visits;
    while(k){
        int a;
        std::cin >> a;
        if(a == -1){
            k--;
            int b = getPos(riches,findGreatest(riches));
            std::cout << b << findGreatest(riches) << std::endl;
            std::cout << riches[b] << std::endl;
            riches[b] = 0; 
        }else{
            riches.push_back(a);
        }
        i++;
    }   
    return 0;
}

Edit : So many views for a solved question , well if possible can anyone take a look at this and this queries?

\$\endgroup\$
6
  • \$\begingroup\$ @ilkkachu probably you have missed one of the tags , any way I updated the question with more info , hope that will help others to answer my query. \$\endgroup\$
    – hellozee
    Oct 7, 2016 at 7:46
  • 1
    \$\begingroup\$ Think being rich and alive a priority. \$\endgroup\$
    – greybeard
    Oct 7, 2016 at 7:58
  • \$\begingroup\$ Do not store and search all the input data. You need to keep just up to M highest wealth values seen so far. So you need to find a data structure for them that will allows fast adding new values while (possibly) discarding the least (if a new value is greater than the least one and a total reaches M values) and fast fetching and removing the highest value. The former action happens up to 100000 times, so is more important than the latter, which happens up to 10000 times. \$\endgroup\$
    – CiaPan
    Oct 7, 2016 at 8:03
  • 2
    \$\begingroup\$ First group of people walks in; prime minister points out that the king is the richest/most corrupt person present and should be beheaded; prime minister is beheaded; end of this policy. \$\endgroup\$ Oct 7, 2016 at 12:50
  • 1
    \$\begingroup\$ So many views for a solved question CR isn't about problem solving, but doing it in style. \$\endgroup\$
    – greybeard
    Oct 8, 2016 at 9:44

4 Answers 4

11
\$\begingroup\$

i am completely useless

  • That int i in main. You never read it. Remove it.
  • The std::vector<int> visits in main. Never used in any way. Remove it.

What are you doing there?

int findGreatest(std::vector<int>vec) {

That sounds good, but why make a copy of the (entire) vector? Better take it by const reference, you don't plan to modify it in that function:

int findGreatest(std::vector<int> const & vec) {

Now what follows is not so good:

    int n = vec.size(); // better use size_t
    int arr[n][n]; // This is NOT C++!
    for(int i=0;i<n;i++){
        arr[n][i] = vec[i]; // Undefined behaviour! Did you mean arr[n-1][i]?
    }
    /* For the rest, I've no idea what you're doing there ... or better WHY you're doing it */
    for(int i=n-2;i>=0;i--){
        for(int j=0;j<n-1;j++){
            if(arr[i+1][j] > arr[i+1][j+1]){
                arr[i][j] = arr[i+1][j];
            }else{
                arr[i][j] = arr[i+1][j+1];
            }
        }
    }
    return arr[0][0];
}

Let's take a step back and see what you're trying to achieve: Finding the greatest element in a vector. To do that, iterate over the vector, tracking the currently greatest element:

int currentMax = std::numeric_limits<int>::min(); // or INT_MIN
for (size_t i = 0; i < vec.size(); ++i) { // May also use a range based loop
  if (vec[i] > currentMax) {
    currentMax = vec[i];
  }
}

As this is a rather useful concept, there's of course a function for it: std::max_element. Though this does not return the value, but rather an iterator (index) to the element with the greatest value. But ...

getPos(riches,findGreatest(riches));

... this is exactly what we want.

Save intermediate values

int b = getPos(riches,findGreatest(riches));
std::cout << b << findGreatest(riches) << std::endl;
std::cout << riches[b] << std::endl;
riches[b] = 0; 

You call your (expensive) findGreatest function twice. Don't. Also you don't stick to the output format specified in the problem statement. Thus we can reduce to

std::vector<int>::iterator wealthiest = std::max_element(std::begin(riches), std::end(riches));
std::cout << *wealthiest << std::endl;
*wealthiest = 0;

But isn't there a faster solution?

With the above, you always search to the entire vector of currently known persons to find the wealthiest of them. In the worst case, e.g. when all N persons enter, and then the king comes to see M people beheaded, you're basically searching M times through the whole vector, thus you have O(M * N).

To improve on that: Nothing states that the persons must stay in the order in which they entered the room. Whenever a new person enters the room, let it stand so that all persons in front of it are wealthier, and all persons behind it are less wealthy. When the king comes, behead the first (as it has no one in front, there's no wealthier person). Basically, keep the collection of citizens sorted according to their wealth.

There are multiple ways of doing so:

  • Use not a vector but a sorted container, e.g. a std::priority_queue or (since there are no two citizens with the same wealth) a std::set.
  • Keep a heap inside your vector: std::make_heap.
  • Sort your vector whenever the king comes. (Though this only works good if sorting an already almost sorted sequence is fast)

Random improvements

  • You know how many citizens there'll be. Reserve space for them beforehand.
\$\endgroup\$
7
  • \$\begingroup\$ Thanks , the find greatest function gets the greatest number by a backtracking algorithm which takes logarithmic time which is faster than the linear search thats why I implemented that ,and that max_element function , yeah I just saw that tomorrow . Anyway int arr[n][n]; is C++ or may be kind of C but they are easy to manipulate than multidimensional vectors , at least for me. \$\endgroup\$
    – hellozee
    Oct 7, 2016 at 13:32
  • 3
    \$\begingroup\$ @KuntalMajumder There can't be a logarithmic time algorithm for finding the max element in an unsorted array since you have to at least look at all the elements in the array to determine the maximum. That puts a strict lower bound of O(n). The algorithm you have there is definitely O(n²) because of the nested for loop. \$\endgroup\$
    – Kyle
    Oct 7, 2016 at 14:31
  • \$\begingroup\$ +1 for the std::priority_queue. I wonder how much of an impact on performance the used underlying container has. Something that allows cheap removal of the first or last element seems to be beneficial. \$\endgroup\$ Oct 7, 2016 at 14:38
  • 1
    \$\begingroup\$ @KuntalMajumder Well, even if it was O(n log n) that's still worse than O(n). Also, when you say something is "logarithmic" that means O(log n), not O(n log n). O(n log n) is sometimes referred to as "linearithmic" (although I don't really hear it referred to that as much as I just hear "n log n", which is shorter anyway). \$\endgroup\$
    – Kyle
    Oct 7, 2016 at 14:52
  • \$\begingroup\$ Thanks for the tips and the corrections , can you help me to sort out this question ? \$\endgroup\$
    – hellozee
    Oct 7, 2016 at 15:39
4
\$\begingroup\$

I think you should not search for the greatest value, but rather keep your array sorted. But first things first:

  1. Use descriptive names. Even if the problems normally use single letters this is bad practise

    int numPeople, numVisits;
    std::cin >> numPeople >> numVisits;
    std::vector<int> people;
    std::vector<int> beheadedWealth;
    
  2. You can already reserve the memory for the vectors as you know their maximal size:

    people.reserve(numPeople); // lets be pessimistic about that
    beheadedWealth.reserve(numVisits);
    
  3. Whats the purpose of i?

  4. You loop does strange things, as it calls findGreatest twice, which is unnecessary. Instead just insert the new values into the vector in a sorted fashion:

    void insert_sorted(std::vector<int> &vec, int wealth) {
        vec.insert(std::upper_bound(vec.begin(), vec.end(), wealth), wealth);
    }
    

    Therewith you always know that the richest person is the one at the end of the people vector and your loop becomes:

    while (numVisits != 0) {
        int input;
        std::cin >> input;
        if (input == -1) {
            beheadedWealth.push_back(people.back());
            people.pop_back();
            numVisits--;
        } else {
            insert_sorted(people, input);
        }
    }
    
  5. Now that you have a vector with all the wealth of the beheaded people you can easily print them out:

    for (auto &elem : beheadedWealth) {
        std::cout << elem << "\n";
    }
    

    Please note that i use "\n" instead of std::endl as this doesnt flush the stream.

All put together we get:

#include <iostream>
#include <vector>
#include <algorithm>

void insert_sorted(std::vector<int> &vec, int wealth) {
    vec.insert(std::upper_bound(vec.begin(), vec.end(), wealth), wealth);
}

int main () {
    int numPeople, numVisits;
    std::cin >> numPeople >> numVisits;
    std::vector<int> people;
    people.reserve(numPeople);

    std::vector<int> beheadedWealth;
    beheadedWealth.reserve(numVisits);

    while (numVisits != 0) {
        int input;
        std::cin >> input;
        if (input == -1) {
            beheadedWealth.push_back(people.back());
            people.pop_back();
            numVisits--;
        } else {
            insert_sorted(people, input);
        }
    }

    for (auto &elem : beheadedWealth) {
        std::cout << elem << "\n";
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ Thanks , should keep that sorted and keep away from implementing own algos until necessary , will keep that in mind \$\endgroup\$
    – hellozee
    Oct 7, 2016 at 13:35
3
\$\begingroup\$

I would think that a heap would be a better choice of data structure for this problem.

#include <iostream>
#include <vector>
#include <algorithm>

int main (int argc, char const* argv[])
{
    int n, k;
    std::cin >> n >> k;
    std::vector<int> v;
    std::make_heap (v.begin(), v.end());
    while (k && n) {
        int a;
        std::cin >> a;
        if (a == -1) {
            k--;
            int x = v.front();
            std::cout << x << '\n';
            std::pop_heap(v.begin(),v.end()); v.pop_back();
        } else {
            n--;
            v.push_back(a); std::push_heap(v.begin(),v.end());
        }
    }
    return 0;
}

Here is what the data structure looks like if you insert 20 and then 1 through 19. This ends up being 20 writes and 35 moves.

20 
20  1                                                 # add  1 
20  1  2                                              # add  2 
20  3  2  1                                           # add  3, move 1 
20  4  2  1  3                                        # add  4, move 1 
20  4  5  1  3  2                                     # add  5, move 1 
20  4  6  1  3  2  5                                  # add  6, move 1 
20  7  6  4  3  2  5  1                               # add  7, move 2 
20  8  6  7  3  2  5  1  4                            # add  8, move 2 
20  9  6  7  8  2  5  1  4 3                          # add  9, move 2 
20 10  6  7  9  2  5  1  4 3 8                        # add 10, move 2 
20 10 11  7  9  6  5  1  4 3 8 2                      # add 11, move 2 
20 10 12  7  9 11  5  1  4 3 8 2 6                    # add 12, move 2
20 10 13  7  9 11 12  1  4 3 8 2 6 5                  # add 13, move 2
20 10 14  7  9 11 13  1  4 3 8 2 6 5 12               # add 14, move 2
20 15 14 10  9 11 13  7  4 3 8 2 6 5 12 1             # add 15, move 3
20 16 14 15  9 11 13 10  4 3 8 2 6 5 12 1 7           # add 16, move 3
20 17 14 16  9 11 13 10 15 3 8 2 6 5 12 1 7 4         # add 17, move 3
20 18 14 17  9 11 13 10 16 3 8 2 6 5 12 1 7 4 15      # add 18, move 3
20 19 14 17 18 11 13 10 16 9 8 2 6 5 12 1 7 4 15 3    # add 19, move 3

If we just keep the vector ordered then for the worst case we have to move 171 memory locations.

20
20  1
20  2  1                                                       # move  1
20  3  2  1                                                    # move  2
20  4  3  2  1                                                 # move  3
20  5  4  3  2  1                                              # move  4
20  6  5  4  3  2  1                                           # move  5
20  7  6  5  4  3  2  1                                        # move  6
20  8  7  6  5  4  3  2  1                                     # move  7
20  9  8  7  6  5  4  3  2  1                                  # move  8
20 10  9  8  7  6  5  4  3  2  1                               # move  9
20 11 10  9  8  7  6  5  4  3  2  1                            # move 10
20 12 11 10  9  8  7  6  5  4  3  2  1                         # move 11
20 13 12 11 10  9  8  7  6  5  4  3  2  1                      # move 12
20 14 13 12 11 10  9  8  7  6  5  4  3  2  1                   # move 13
20 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1                # move 14
20 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1             # move 15
20 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1          # move 16
20 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1       # move 17
20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1    # move 18
\$\endgroup\$
1
  • \$\begingroup\$ Thanks , but I havent still got how usefull heap is or how does it properly works , for me it seems to take more time and space , will take a look at it. \$\endgroup\$
    – hellozee
    Oct 7, 2016 at 13:36
1
\$\begingroup\$

@Daniel Jour suggested in his great answer to use a std::priority_queue. I thought I give that a try and turn it into a full working example code. It's pretty much a straightforward implementation of the problem description.

#include <iostream>
#include <queue>

int main ()
{
    int N = 0, M = 0, input = 0;

    std::priority_queue<int> sortedWealthList;

    std::cin >> N >> M;

    for(int inputLine = 0; inputLine < N + M; ++inputLine)
    {
        std::cin >> input;

        if (input == -1)
        {
            std::cout << sortedWealthList.top() << "\n";
            sortedWealthList.pop();
        }
        else
        {
            sortedWealthList.push(input);
        }
    }
}

That scores 100 on the IARCS Problems Archive, whatever that means.

\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.