-4

The problem is similar to subset-sum in which we determine whether or not a list of number has a subset which produces a given sum, but in this case, we are allowed to create a subset from elements of list p only if it is equal or greater than the respective element in other list q. Also instead of sum of values of elements, the k is number of elements that can be added. so if k is 3 we need to select 3 elements from list p but those elements cannot be smaller than respective element of list q. I am new to dynamic programming and knapsack please help me.

public static List<Integer> kthPerson(int k, List<Integer> p, List<Integer> q) {

    List<Integer> q1 = new ArrayList<>();
    q1.addAll(q);
    Collections.sort(q1);

    int maxQ = q1.get(q1.size()-1);

    List<Integer> res = new ArrayList<>();

    int[][] dp = new int[k+1][maxQ+1];
    for(int[] d:dp){
        Arrays.fill(d, 0);
    }

    for (int u = 0; u < maxQ; u++) {
        int count = 0;
        for (int i = 0; i < p.size(); i++){
            if (p.get(i) >= u){
                dp[count][u] = i+1;
                count++;
            }
            if (count == k){
                break;
            }
        }
    }

    for (int s = 0; s < q.size(); s++) {
        res.add(dp[k-1][q.get(s)]);
    }
    return res;
}

/*if you want to test*/
public static void main(String args[]) {



        List<Integer> p = new ArrayList<>();

        p.add(1);
        p.add(4);
        p.add(4);
        p.add(3);
        p.add(1);
        p.add(2);
        p.add(6);

        List<Integer> q = new ArrayList<>();

        q.add(1);
        q.add(2);
        q.add(3);
        q.add(4);
        q.add(5);
        q.add(6);
        q.add(7);

        kthPerson(2, p, q);


    }
 /*you will get 
2
3
3
3
0
0
0. which is desired result but when the input is really large I get the java heap error*/


theUturn
  • 1,101
  • 2
  • 12
  • 25
  • 1
    You will have to give us more information and explanation, the question right now is not clear, you can read about what you are trying to do in if you will search [knapsack dynamic programing java](https://medium.com/@ssaurel/solving-the-knapsack-problem-in-java-c985c71a7e64). – Yonlif Mar 24 '20 at 14:54
  • 1
    Also, since I instinctively expect "endless recursion" to be the root-cause problem here, you must supply **all** of the relevant source-code, not just the `kthPerson` function, which itself does not appear to be recursive. SO is not a debugging service: we need *precise* questions, not "there's a bug in here somewhere, so let me throw it at you and you just fix it." – Mike Robinson Mar 24 '20 at 15:21
  • @MikeRobinson there is no recursion here I am trying to do a bottom-up dynamic programming by adding all possible values up maximum in the given list and then simply piking result at the end – theUturn Mar 24 '20 at 15:36
  • How large exactly are the inputs that are causing the error? The java max heap size is around 2 GB (!), if the number of people is 250 and the largest q is 10,000, it can crash. You can notice that you do not really need `k+1` lines, just the last 2, that will help but the time complexity will remain high. – Yonlif Mar 24 '20 at 16:36
  • Please help us to help you @theUturn , explain what does your output mean? Give the variable meaningful names. Also are you sure dynamic programing needed? – Yonlif Mar 27 '20 at 17:45
  • Could you please share the hackerrank link? – Yonlif Mar 27 '20 at 18:24

2 Answers2

2

Well the truth is you do not need dynamic programing for this question. No case is actually based on its previous cases.
Therefore you can write this function:

public static List<Integer> kthPersonNew(int k, List<Integer> p, List<Integer> q) {
    List<Integer> res = new ArrayList<>();

    for (int limit : q) {
        int count = 0;
        boolean added = false;
//      For each person index
        for (int i = 0; i < p.size(); i++) {
//          If this person is higher than the needed limit - count it
            if (p.get(i) >= limit) {
                count++;
//              If you have counted k persons than add the kth person to res and break
                if (count == k) {
                    res.add(i + 1);
                    added = true;
                    break;
                }
            }
        }
//      In case no one was added than add 0
        if (!added) {
            res.add(0);
        }
    }
    return res;

}

And it is giving the same output for the test case you have provided.

p = [1, 4, 4, 3, 1, 2, 6]
q = [1, 2, 3, 4, 5, 6, 7]
=>
res = [2, 3, 3, 3, 0, 0, 0]

Now I am certain this program will not cause an java.lang.OutOfMemoryError since you are only using one array and it is the same length as q.
This should solve your problem.


Edit:
It appears that the new algorithm is good memory wise but isn't fast enough, assuming q is always growing we can use this and make sure the time complexity is always smaller than O(q * k + n). Since every element that is not fitting in the current limit will not fit at the future limits to come.

We will need an helper class:

class Element{
    private int index;
    private int value;

    public Element(int index, int value){
        this.index = index;
        this.value = value;
    }

    public int getIndex(){
        return this.index;
    }

    public int getValue(){
        return this.value;
    }

    @Override
    public String toString(){
        return value + " " + index;
    }

    public void print(){
        System.out.println(this);
    }
}

So that we will be able to remember the old index of variables even after deleted.

And the new function:

public static List<Integer> kthPersonFast(int k, List<Integer> p, List<Integer> q) {
    List<Integer> res = new ArrayList<>();

    List<Element> elements = new LinkedList<Element>();
    for (int i = 0; i < p.size(); i++) {
        elements.add(new Element(i, p.get(i)));
    }

    for (int limit : q) {
        int count = 0;
        boolean added = false;
//          For each person         
        Iterator<Element> itr = elements.iterator(); 
        while (itr.hasNext()) { 
            Element person = itr.next(); 

//              If this person is higher than the needed limit - count it
            if (person.getValue() >= limit) {
                count++;
//                  If you have counted k persons than add the kth person to res and break
                if (count == k) {
                    res.add(person.getIndex() + 1);
                    added = true;
                    break;
                }
            } else {
//                  If the person didn't fit for that limit it will not fit to any other limit
//                  since we are assuming the limits are always growing
                itr.remove();
            }
        }
//          In case no one was added than add 0
        if (!added) {
            res.add(0);
        }
    }
    return res;
}

You will also need to import:

import java.util.Iterator;
import java.util.LinkedList;

That should be it.

Good Luck

Yonlif
  • 1,770
  • 1
  • 13
  • 31
  • Thanks @Yonlif but this approach creates Time limit exceed situation ..both array can be of size 100000 – theUturn Mar 29 '20 at 06:46
  • Oh, no problem I will lower the time limit. You should know that the current version has the same time complexity as for your algorithm. – Yonlif Mar 29 '20 at 11:00
  • @theUturn Do you need to output all of this array? Could you please share the hackerrank link? – Yonlif Mar 29 '20 at 21:34
  • the link has expired, unfortunately .. but I am sure I applied same direct logic at first which led TLE and then I went for dynamic programming approach otherwise why would I? – theUturn Mar 30 '20 at 04:04
  • @theUturn Does that still outputs TLE? – Yonlif Mar 30 '20 at 06:57
  • @theUturn If the link has expired, how are you able to submit a solution and get a TLE? – jrook Mar 31 '20 at 06:16
  • so you think I did not try this general approach before doing dynamic programming to solve the approach... I posted this question after the test is done and the link has expired – theUturn Mar 31 '20 at 09:49
  • I understand you have tried the general approach, but maybe you have tried it in a way that achieved a higher time complexity (`O(q * p)`). If you will read the edit you will see that the new time complexity is only `O(p * k + p)`. – Yonlif Mar 31 '20 at 15:01
  • Actually this assumption is not correct: "If the person didn't fit for that limit it will not fit to any other limit since we are assuming the limits are always growing", in some of the test cases of this exercise there are about 100k limits in a random way. This solution still gives TLE – JeisonG Nov 03 '20 at 23:12
0

You may need to look in other parts of your program to see if you can remove unused objects. In this case, we would need to see more of the code. That being said you might benefit from the following if you have not tried already:

if you are using a 64-bit JVM you can simply increase your heap size.

java -Xmx4G -jar MyProgram.jar

https://javarevisited.blogspot.com/2011/05/java-heap-space-memory-size-jvm.html

  • This actually might help to the memory complexity but in competitive programing the user does not control the compilation, furthermore this does not help for the time complexity which will remain the same. – Yonlif Apr 01 '20 at 22:42