4
\$\begingroup\$

PRAMP Award Budget Cuts

The awards committee of your alma mater (i.e. your college/university) asked for your assistance with a budget allocation problem they’re facing. Originally, the committee planned to give N research grants this year. However, due to spending cutbacks, the budget was reduced to newBudget dollars and now they need to reallocate the grants. The committee made a decision that they’d like to impact as few grant recipients as possible by applying a maximum cap on all grants. Every grant initially planned to be higher than cap will now be exactly cap dollars. Grants less or equal to cap, obviously, won’t be impacted.

Given an array grantsArray of the original grants and the reduced budget newBudget, write a function findGrantsCap that finds in the most efficient manner a cap such that the least number of recipients is impacted and that the new budget constraint is met (i.e. sum of the N reallocated grants equals to newBudget).

Example:

input: grantsArray = [2, 100, 50, 120, 1000], newBudget = 190
output: 47 # and given this cap the new grants array would be
 # [2, 47, 47, 47, 47]. Notice that the sum of the
 # new grants is indeed 190

My approach:

import java.util.Arrays;
class Solution {
 static double findGrantsCap(double[] grantsArray, double newBudget) {
 // your code goes here
 int len = grantsArray.length;
 Arrays.sort(grantsArray);
 //Calculate the newBudget
 double modBudget = newBudget;
 double cap;
 double sum = 0;
 double avg; 
 for (double elem: grantsArray)
 sum += elem;
 avg = sum/len;
 if( avg == (int)newBudget)
 return (newBudget)/len;
 else
 {
 Arrays.sort(grantsArray);
 modBudget -= grantsArray[0];
 cap = modBudget/(len - 1);
 for( int i = 1; i < len-1; i++ )
 {
 System.out.println(cap);
 if( (int)cap > grantsArray[i] )
 {
 modBudget -= grantsArray[i];
 cap = modBudget/(len - i - 1);
 }
 }
 }
 return cap; 
 }
 public static void main(String[] args) {
 double [] grantsArray = {2,100,50,120,167};
 double newBudget = 400;
 System.out.println(findGrantsCap(grantsArray,newBudget));
 }
}

I have the following questions with regards to the above code:

  1. How can I further improve the algorithm, time or space wise?

  2. Is my solution satisfying all the test cases and why is it failing if any?

  3. Have I made any gross violations to the Java Coding conventions?

Reference

Mast
13.8k12 gold badges57 silver badges127 bronze badges
asked May 12, 2018 at 19:04
\$\endgroup\$
3
  • \$\begingroup\$ "Is my solution satisfying all the test cases and why is it failing if any": have you ran tests yourself? \$\endgroup\$ Commented May 12, 2018 at 20:52
  • \$\begingroup\$ @Koekje, Yes, I have run some tests given on the website and my algorithm passes most of them. I am unsure as in how I can solve for the remaining cases. My sincere apologies if this is defying the rules of this web forum. \$\endgroup\$ Commented May 13, 2018 at 4:55
  • 1
    \$\begingroup\$ It is written that you should have code which works, to the best of your knowledge. Which you know it does not. So it would seem it does not completely conform. I have no idea how good/bad it is in this case, or what to do :D \$\endgroup\$ Commented May 13, 2018 at 23:41

6 Answers 6

2
\$\begingroup\$

I believe that the problem in the first place is not well defined (or we are trying to solve a different problem). And I mean...

The problem states the following:

write a function findGrantsCap that finds in the most efficient manner a cap such that the least number of recipients is impacted and that the new budget constraint is met

So it focuses on the recipients impacted and not on the amounts.

e.g

Input: grantsArray = [2, 100, 50, 120, 1000], newBudget = 190

If I pick for cap the 100 value the array becomes like this

Input: grantsArray = [2, 100, 50, 1, 37], newBudget = 190

Now only 2 recipients affected (of course the sum of the array elements meets the newBudget constraint), but it's totally unfair.

answered Aug 30, 2018 at 17:55
\$\endgroup\$
3
  • 1
    \$\begingroup\$ "If I pich" --- was that supposed to be "If I pitch"? \$\endgroup\$ Commented Aug 30, 2018 at 18:56
  • \$\begingroup\$ Just "pick" :) \$\endgroup\$ Commented Aug 31, 2018 at 8:29
  • 2
    \$\begingroup\$ I also found this question confusing, but your solution doesn't work because of this clause: "Every grant initially planned to be higher than cap will now be exactly cap dollars." \$\endgroup\$ Commented Nov 20, 2018 at 12:00
1
\$\begingroup\$

1) Code from https://repl.it/@sixxta/Award-Budget-Cuts suggests you could sort array in descending order and keep decreasing maximum single grant until there is no overspending. I don't know if you version, starting with smallest value, works correctly.

2) for cases:

{14,15,16,17,18,19}, 97
{14,15,16,17,18,19}, 98
{14,15,16,17,18,19}, 99

your code returns:

17.33
17.66
18.5

While the proper answer is:

17
18
19

3) the algorithm is optimal in terms of time and space, unless the is some way to remove sorting that I am not aware of.

4) you are looking for exact solution, using floating point variables isn't a good idea

 if( avg == (int)newBudget)
 return (newBudget)/len;

This check doesn't make any sense. You probably wanted to compare with newBudget/len but even then you would return 2 for {1,2,3},6 when right answer is 3.

answered May 16, 2018 at 10:41
\$\endgroup\$
1
  • \$\begingroup\$ Thanks for the advice. @user158037. I will keep this important floating point issue in mind. \$\endgroup\$ Commented May 19, 2018 at 16:06
1
\$\begingroup\$

There is a better approach for that problem.

First start by sorting the array, next traverse the array, for every element that is lower than the cap (your cap starts as the avg of budget/array.length) reduce it from your current funds and recalculate the cap (i.e. now the cap equals to budget/(array.length - (i+1))).

Once you got to the end of the array or to the first element that is higher than your current cap you just return the last cap you have in hand.

So all in all this is your solution:

 static double findGrantsCap(double[] arr, double newBudget) {
 // your code goes here
 Arrays.sort(arr); // O(N*LogN)
 int n = arr.length;
 double cap = newBudget/n;
 for(int i = 0; i < n; i++) { // O(N)
 if(arr[i] < cap) {
 newBudget -= arr[i];
 cap = (newBudget/(n-(1+i)));
 }else {
 arr[i] = cap;
 }
 }
 return cap;
 }

Adding all the numbers on the array will be the newBudget since for every lower number we subtract it from the total and everything above just gets capped (essentially gets to be the average from what we got left with divided with the total elements we got left with - adding those will be the remains of the budget we got up to them).

t3chb0t
44.6k9 gold badges84 silver badges190 bronze badges
answered Jul 23, 2018 at 17:16
\$\endgroup\$
1
  • \$\begingroup\$ This will throw a Division by Zero error on cap = (newBudget/(n-(1+i))); \$\endgroup\$ Commented Oct 18, 2019 at 5:52
1
\$\begingroup\$

This is my solution with complexity: time \$O(N*log(N))\$, space \$O(1)\$.

static double findGrantsCap(double[] grantsArray, double newBudget) {
if (grantsArray == null || 
 grantsArray.length == 0 || 
 (grantsArray.length == 1 && grantsArray[0] <= newBudget)) {
 return newBudget;
}
Arrays.sort(grantsArray);
double cap = newBudget / grantsArray.length;
double allocated = 0.0;
for (int i=0; i<grantsArray.length; i++) {
 double currentRequestValue = grantsArray[i];
 if (currentRequestValue <= cap) {
 allocated += currentRequestValue;
 int divisor = (grantsArray.length - i - 1);
 // If last index in the array is below the cap divisor will be "0"
 // It means that all the items in the carousel can be allocated as they are
 cap = divisor != 0 ? (newBudget - allocated) / divisor : currentRequestValue;
 }
}
return cap;
}
alecxe
17.5k8 gold badges52 silver badges93 bronze badges
answered Dec 12, 2018 at 16:46
\$\endgroup\$
0
\$\begingroup\$

I think you don't need to sort the input. you can solve this question in \$O(n)\$:

static double findGrantsCap(double[] grantsArray, double newBudget) {
 int len = grantsArray.length;
 double budget = newBudget;
 double cap = budget * 1.0 / len * 1.0;
 for (int i = 0; i < len; i++) {
 if (grantsArray[i] < cap) {
 budget -= grantsArray[i];
 cap = budget * 1.0 / (len - i - 1);
 }
 else {
 budget -= cap; 
 }
 }
 return cap;
} 
dfhwze
14.1k3 gold badges40 silver badges101 bronze badges
answered Jul 20, 2019 at 19:03
\$\endgroup\$
1
  • \$\begingroup\$ I came up with this solution too, but this doesn't works for some test cases. So sorting the input array is the perfect solution for this question. Failed test case is stated below.... Actual: [170.0, 170.0, 150, 173.33333333333334, 130, 110, 208.88888888888889, 208.88888888888886, 117] 117 Expected: 211 \$\endgroup\$ Commented Feb 1, 2021 at 3:26
0
\$\begingroup\$

I have a greedy approach, this calculates a lower bound that I can pick initially, whenever the current grant is less than this lower bound, I can carry over the excess to remaining values, at the same time re-calculating the lower bound. Since my array is sorted, I can return as soon as I find a grant which is greater than the lower bound.

 static double findGrantsCap(double[] grantsArray, double newBudget) {
 if(grantsArray == null || grantsArray.length == 0 || newBudget == 0) {
 return 0.0;
 }
 Arrays.sort(grantsArray);
 double cap = 0.0;
 double minAllocate = newBudget / grantsArray.length; 
 int allocated = 0; 
 for(double oldGrant : grantsArray) {
 if(oldGrant <= minAllocate) {
 newBudget -= oldGrant;
 allocated ++;
 minAllocate = newBudget / (grantsArray.length - allocated);
 }
 else {
 return minAllocate;
 }
 }
 return grantsArray[grantsArray.length -1];
 }
greybeard
7,3913 gold badges21 silver badges55 bronze badges
answered May 18, 2020 at 7:31
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.