Competitive programming - Prepare the perfect curry with ingredients P, Q, and R - algorithm

Recently i got a competetive programming task which i couldn't manage to complete. Just curious to know the best solution for the problem
"A" is a zero-indexed array of N integers.
Elements of A are integers within the range [−99,999,999 to 99,999,999]
The 'curry' is a string consisting of N characters such that each character is either 'P', 'Q' or 'R' and the
corresponding index of the array is the weight of each ingredient.
The curry is perfect if the sum of the total weights of 'P', 'Q' and 'R' is equal.
write a function
makeCurry(Array)
such that, given a zero-indexed array Array consisting of N integers, returns the perfect curry of this array.
The function should return the string "noLuck" if no perfect curry exists for that Array.
For example, given array Array such that
A[0] = 3 A[1] = 7 A[2] = 2 A[3] = 5 A[4] = 4
the function may return "PQRRP", as explained above. Given array A such that
A[0] = 3 A[1] = 6 A[2] = 9
the function should return "noLuck".
The approach i tried was this
import collections
class GetPerfectCurry(object):
def __init__(self):
self.curry = ''
self.curry_stats = collections.Counter({'P': 0, 'Q': 0, 'R': 0})
pass
def get_perfect_curry(self, A):
if len(A) == 0:
return "noLuck"
A.sort(reverse=True)
for i, ele in enumerate(A):
self.check_which_key_to_add_new_element_and_add_element(ele)
if self.curry_stats['P'] == self.curry_stats['Q'] == self.curry_stats['R']:
return self.curry
else:
return "noLuck"
def check_which_key_to_add_new_element_and_add_element(self, val):
# get the maximum current value
# check if addition of new value with any of the other two key equals the max value
# if yes then add that value and append the key in the curry string
current_max_key = max(self.curry_stats, key=self.curry_stats.get)
check_for_equality = False
key_to_append = None
for key, ele in enumerate(self.curry_stats):
if ele != current_max_key:
if self.curry_stats[ele] + val == self.curry_stats[current_max_key]:
check_for_equality = True
key_to_append = ele
if check_for_equality:
self.curry_stats.update(str(key_to_append) * val)
self.curry += str(key_to_append)
pass
else:
# if no value addition equals the current max
# then find the current lowest value and add it to that key
current_lowest_key = min(self.curry_stats, key=self.curry_stats.get)
self.curry_stats.update(str(current_lowest_key)*val)
self.curry += str(current_lowest_key)
if __name__ == '__main__':
perfect_curry = GetPerfectCurry()
A = [3, 7, 2, 5, 4]
# A = [3, 6, 9]
# A = [2, 9, 6, 3, 7]
res = perfect_curry.get_perfect_curry(A)
print(res)
But it was incorrect. Scratching my head for the past four hours for the best solution for this problem

A possible algorithm is as follows:
Sum the weights. If it's not a multiple of 3, no luck. If it is, divide by 3 to get the target.
Find subsets of A that add up to target. For such subsets, remove it and you get B. Find a subset of B that adds up to target.
Here's a Java implementation (I'm not a Python guy, sorry):
import java.util.Arrays;
public class Main
{
// Test if selected elements add up to target
static boolean check(int[] a, int selection, int target)
{
int sum = 0;
for(int i=0;i<a.length;i++)
{
if(((selection>>i) & 1) == 1)
sum += a[i];
}
return sum==target;
}
// Remove the selected elements
static int[] exclude(int[] a, int selection)
{
int[] res = new int[a.length];
int j = 0;
for(int i=0;i<a.length;i++)
{
if(((selection>>i) & 1) == 0)
res[j++] = a[i];
}
return Arrays.copyOf(res, j);
}
static String getCurry(int[] a)
{
int sum = 0;
for(int x : a)
sum += x;
if(sum%3 > 0)
return "noLuck";
int target = sum/3;
int max1 = 1<<a.length; // 2^length
for(int i=0;i<max1;i++)
{
if(check(a, i, target))
{
int[] b = exclude(a, i);
int max2 = 1<<b.length; // 2^length
for(int j=0;j<max2;j++)
{
if(check(b, j, target))
return formatSolution(i, j, a.length);
}
}
}
return "noLuck";
}
static String formatSolution(int p, int q, int len)
{
char[] res = new char[len];
Arrays.fill(res, 'R');
int j = 0;
for(int i=0;i<len;i++)
{
if(((p>>i) & 1) == 1)
res[i] = 'P';
else
{
if(((q>>j) & 1) == 1)
res[i] = 'Q';
j++;
}
}
return new String(res);
}
public static void main(String[] args)
{
// int[] a = new int[]{3, 7, 2, 5, 4};
// int[] a = new int[]{1, 1, 2, -1};
int[] a = new int[]{5, 4, 3, 3, 3, 3, 3, 3};
System.out.println(getCurry(a));
}
}
You can test it here.

Hereafter so many years I'm writing code for js for needed people. (TBH I took the ref of the accepted answer)
As he mentioned, A possible algorithm is as follows:
Sum the weights. If it's not a multiple of 3, no luck. If it is, divide by 3 to get the target.
Find subsets of A that add up to target. For such subsets, remove it and you get B. Find a subset of B that adds up to target.
// Test if selected elements add up to target
function check(a, selection, target)
{
let sum = 0;
for(let i=0;i<a.length;i++)
{
if(((selection>>i) & 1) == 1)
sum += a[i];
}
return sum==target;
}
// Remove the selected elements
function exclude(a, selection)
{
let res = [a.length];
let j = 0;
for(let i=0;i<a.length;i++)
{
if(((selection>>i) & 1) == 0)
res[j++] = a[i];
}
return res
}
function getCurry(a)
{
let sum = a.reduce((accumulator, currentValue) => accumulator + currentValue);
if(sum%3 > 0)
return "noLuck";
let target = sum/3;
let max1 = 1<<a.length; // 2^length
for(let i=0;i<max1;i++)
{
if(check(a, i, target))
{
let b = exclude(a, i);
let max2 = 1<<b.length; // 2^length
for(let j=0;j<max2;j++)
{
if(check(b, j, target))
return formatSolution(i, j, a.length);
}
}
}
return "noLuck";
}
function formatSolution(p, q, len)
{
let res = new Array(len)
res.fill('R')
let j = 0;
for(let i=0;i<len;i++)
{
if(((p>>i) & 1) == 1)
res[i] = 'P';
else
{
if(((q>>j) & 1) == 1)
res[i] = 'Q';
j++;
}
}
return new String(res);
}
// let a = [3, 7, 2, 5, 4]
// let a = [1, 1, 2, -1]
let a = [5, 4, 3, 3, 3, 3, 3, 3]
getCurry(a)

Related

maximum sum of subsequence

Given an integer array, find the maximum sum of subsequence where the subsequence contains no element at adjacent positions.
Input: { 1, 2, 9, 4, 5, 0, 4, 11, 6 }
Output: The maximum sum is 26
The maximum sum is formed by subsequence { 1, 9, 5, 11 }
My below code is working fine .helper2 method is same as findMaxSumSubsequence but with memoisation.Both these methods call themselves recursively exploring all subsets i.e take the element at ith pos or not take the element at ith position.
private int findMaxSumSubsequence(int[] arr ,int i ,boolean flag) {
if( i>=arr.length)
return 0;
int incAns=0;
if(!flag)
{
incAns=findMaxSumSubsequence(arr,i+1,true) + arr[i];
}
int exAns=findMaxSumSubsequence(arr,i+1,false) ;
return Math.max(incAns, exAns);
But when i try to memoise the code I get the wrong answer for
helper2(nums,0,false) //method call
nums={1,2} after memoisation i get answer as 1 .Correct answer is 2.
int[] memo is initialised with -1 .
int[] memo = new int[101];
private int helper2(int[] arr ,int i ,boolean flag) {
if( i>=arr.length)
return memo[i]=0;
if(memo[i]!=-1)
return memo[i];
int incAns=0;
if(!flag)
{
incAns=helper2(arr,i+1,true) + arr[i];
}
int exAns=helper2(arr,i+1,false) ;
memo[i]= Math.max(incAns, exAns);
return Math.max(incAns, exAns);
You are missing the second memoization parameter 'flag', that's why you have a wrong answer, it should be:
int[][] memo = new int[101][2];
instead of
int[] memo = new int[101];
code:
int[][] memo = new int[101][2];
int [] arr;
private int helper2(int i ,boolean flag) {
if (i >= arr.length)
return 0;
if (memo[i][flag? 1 : 0] != -1) return memo[i][flag ? 1 : 0];
int incAns = 0;
if (!flag) {
incAns = helper2( i + 1, true) + arr[i];
}
int exAns = helper2( i + 1, false);
return memo[i][flag? 1 : 0] = Math.max(incAns, exAns);
}
public static void main(String[] args) {
arr = new int[]{1, 2};
for(int i = 0; i < 100; i++) Arrays.fill(memo[i], -1);
out.println(helper2(0, false));
}
Also no need to add the array as a parameter in the recursive function because it's static and don't change, otherwise you will get Memory Limit Exceeded.

How to get the number of the Subarray with at least K different numbers?

The question is:
"Given an array A only contains integers Return the number of subarrays that contain at least k different numbers. Subarrays cannot be duplicated."
Example:
input array = {1, 2, 3, 4, 2} k = 3
output: 4
Explanation:
the number of the Subarray with at least K different numbers should be 4,
which are [1, 2, 3] [2, 3, 4] [3, 4, 2] [1, 2, 3, 4]
Right now what I can do is just find about the number of the subarray with exactly K different numbers:
class Solution {
public int subarraysWithKDistinct(int[] A, int K) {
return atMostK(A, K) - atMostK(A, K - 1);
}
private int atMostK(int[] A, int K) {
int i = 0, res = 0;
Map<Integer, Integer> count = new HashMap<>();
for (int j = 0; j < A.length; ++j) {
if (count.getOrDefault(A[j], 0) == 0) K--;
count.put(A[j], count.getOrDefault(A[j], 0) + 1);
while (K < 0) {
count.put(A[i], count.get(A[i]) - 1);
if (count.get(A[i]) == 0) K++;
i++;
}
res += j - i + 1;
}
return res;
}
}
But when the input be:
array = {1, 2, 3, 4, 2} k = 2
my code will not work correctly, but I don't know where to change. Any thoughts? Thanks!
Update: thanks to #MBo and others' answers, I used 2 pointers to fix this problem, but still cannot get the right answer with:
array = {1, 2, 3, 4, 2} k = 3 -> output: 6 (should be 4)
It looks like there are some duplicated substrings be counted, but I don't know how to fix it.
class Solution {
public static void main(String[] args) {
int[] A = {1, 2, 3, 4, 2};
int k = 3;
int res = helper(A, k);
System.out.println(res);
// output is 6, but should be 4
}
private static int helper(int[] A, int k) {
if (A == null || A.length == 0) return 0;
int n = A.length;
int res = 0;
int differentNumbers = 0;
Map<Integer, Integer> counter = new HashMap<>();
int j = 0; // j - 1 is the right point
for (int i = 0; i < n; i ++) {
while (j < n && differentNumbers < k) {
int numOfThisNumber = counter.getOrDefault(A[j], 0);
counter.put(A[j], numOfThisNumber + 1);
if (counter.get(A[j]) == 1) {
differentNumbers ++;
}
j ++;
}
if (differentNumbers == k) {
res += n - j + 1;
}
counter.put(A[i], counter.get(A[i]) - 1);
if (counter.get(A[i]) == 0) {
differentNumbers --;
}
}
return res;
}
}
You can combine your hashmap approach with method of two pointers (indices).
Set both indices into 0 and move right one, updating hashmap counts for values at the right end of interval until hashmap size reaches K. Fix right index.
Now move left index, decreasing counts corresponding to the values at left end. Before every step (including left=0) add size-right to result, because all subarrays starting from left and ending after right, do contain needed number of elements.
When some count becomes 0, remove value from hashmap, and fix left index.
Repeat with right index and so on.

Calculate output vector in a way that for every element i, output[i] is the product of all values of the input vector other than i

Implement the function, that receives as input a List and returns another List of the same length, so that for every element i result[i] is the product of all values of the input vector except the one at the position i in the input vector.
Examples :
input = {1,2,3} //output = {6,3,2}
input = {1,2,3,4} //output = {24,12,8,6}
input = {2,0,2,3} // output = {0,12,0,0}
input = {0,2,0,3} // output = {0,0,0,0}
Tried in java, with some edge cases and multiple zero's in input eg: {2,0,2,3}, {0,2,0,0} etc..
import java.util.*;
public class ProductArray {
public static void main(String [] args) {
System.out.println("This is a debug message");
List<Integer> input = new ArrayList<Integer>();
input.add(3);
input.add(2);
input.add(2);
input.add(1);
input.add(1);
ProductArray productArray = new ProductArray();
List<Integer> results = productArray.arrayProducts(input);
System.out.print(results.toString());
}
public List<Integer> arrayProducts(List<Integer> input){
List<Integer> result = new ArrayList<Integer>();
int arraySize = input != null ? input.size() : 0;
int itr = 0;
int totalProduct = 1;
int zeroCount = 0;
if(arraySize > 2){ // else empty list returned
// loop to find totalProduct and zeroCount
do{
if(input.get(itr) != 0) {
totalProduct *= input.get(itr);
} else {
++zeroCount;
}
itr++;
}while(itr < arraySize);
for(Integer element : input){
if(zeroCount > 1) {
result.add(0);
} else if(zeroCount == 1 ) {
if(element == 0) {
result.add(totalProduct);
} else {
result.add(0);
}
} else if(zeroCount == 0 ) {
result.add(totalProduct/element);
}
}
}
return result;
}
}
It is simple, you just compute the product of all the elements in set1, and then each element i in set2, will be that product divided by the element set1[i]
here is a C++ implementation:
void solve(){
const int input_size = 4;
int set1[input_size] = {1, 2, 3, 4};
int result[input_size];
int product = 1;
for (int i = 0; i < input_size; ++i) {
product = product * set1[i];
}
for (int i = 0; i < input_size; ++i) {
result[i] = product / set1[i];
}
for (int i = 0; i < input_size; ++i) {
cout << result[i] << " ";
}
}
Here's a solution in Ruby, heavily annotated to explain the logic. Several test cases are presented at the bottom. The overall complexity is O(n), where n is the size of the input array/list.
def products(ary)
# Pick out the zeros and count - O(ary.size)
num_zeros = ary.select(&:zero?).size
# Return an array of all zeros if there's more than 1 zero
return Array.new(ary.size, 0) if num_zeros > 1
# Compute the product of all the non-zero values - O(ary.size)
product = ary.inject(1) { |memo, x| x.zero? ? memo : memo * x }
# If there are no zeros, create the answer by mapping each value
# to the product divided by the current value - O(ary.size)
if num_zeros.zero?
return ary.map { |x| product / x }
else
# Otherwise, all values map to zero except the element whose value
# is zero, which maps to the product of the non-zeros - O(ary.size)
return ary.map { |x| x.zero? ? product : 0 }
end
end
p products [3, 2, 1] # [2, 3, 6]
p products [-3, -2, -1] # [2, 3, 6]
p products [-3, 2, -1] # [-2, 3, -6]
p products [2, 4, 0, 6] # [0, 0, 48, 0]
p products [2, 0, 4, 6, 0] # [0, 0, 0, 0, 0]

sum of array elements(any order) equal to k not continuous elements [duplicate]

This question already has an answer here:
subset sum find all subsets that add up to a number
(1 answer)
Closed 2 years ago.
I have 2 input
array: {3,6,9,0,2,1,3} // positive number and can repeat also
Sum = 9
Need to find a combination(order not mandatory) of array element which has total to Sum(here for example it's 9).
Output expected :
{3,6}
{9}
{6,3}
{3,2,1,3}
I am not able to solve it. So, please don't ask for my solution. Please help by solving in java.
This problem can be solved by printing all the subsets with given sum.
Have a look at the following implementation:
// A Java program to count all subsets with given sum.
import java.util.ArrayList;
public class SubSet_sum_problem
{
// dp[i][j] is going to store true if sum j is
// possible with array elements from 0 to i.
static boolean[][] dp;
static void display(ArrayList<Integer> v)
{
System.out.println(v);
}
// A recursive function to print all subsets with the
// help of dp[][]. Vector p[] stores current subset.
static void printSubsetsRec(int arr[], int i, int sum,
ArrayList<Integer> p)
{
// If we reached end and sum is non-zero. We print
// p[] only if arr[0] is equal to sun OR dp[0][sum]
// is true.
if (i == 0 && sum != 0 && dp[0][sum])
{
p.add(arr[i]);
display(p);
p.clear();
return;
}
// If sum becomes 0
if (i == 0 && sum == 0)
{
display(p);
p.clear();
return;
}
// If given sum can be achieved after ignoring
// current element.
if (dp[i-1][sum])
{
// Create a new vector to store path
ArrayList<Integer> b = new ArrayList<>();
b.addAll(p);
printSubsetsRec(arr, i-1, sum, b);
}
// If given sum can be achieved after considering
// current element.
if (sum >= arr[i] && dp[i-1][sum-arr[i]])
{
p.add(arr[i]);
printSubsetsRec(arr, i-1, sum-arr[i], p);
}
}
// Prints all subsets of arr[0..n-1] with sum 0.
static void printAllSubsets(int arr[], int n, int sum)
{
if (n == 0 || sum < 0)
return;
// Sum 0 can always be achieved with 0 elements
dp = new boolean[n][sum + 1];
for (int i=0; i<n; ++i)
{
dp[i][0] = true;
}
// Sum arr[0] can be achieved with single element
if (arr[0] <= sum)
dp[0][arr[0]] = true;
// Fill rest of the entries in dp[][]
for (int i = 1; i < n; ++i)
for (int j = 0; j < sum + 1; ++j)
dp[i][j] = (arr[i] <= j) ? (dp[i-1][j] ||
dp[i-1][j-arr[i]])
: dp[i - 1][j];
if (dp[n-1][sum] == false)
{
System.out.println("There are no subsets with" +
" sum "+ sum);
return;
}
// Now recursively traverse dp[][] to find all
// paths from dp[n-1][sum]
ArrayList<Integer> p = new ArrayList<>();
printSubsetsRec(arr, n-1, sum, p);
}
//Driver Program to test above functions
public static void main(String args[])
{
int arr[] = {3, 6, 9, 0, 2, 1, 3};
int n = arr.length;
int sum = 9;
printAllSubsets(arr, n, sum);
}
}
Output:
[6, 3]
[9]
[0, 6, 3]
[0, 9]
[1, 2, 6]
[1, 2, 0, 6]
[3, 6]
[3, 0, 6]
[3, 1, 2, 3]
[3, 1, 2, 0, 3]

Find all M-length sets of positive integers that sum to N

The problem I'm trying to solve is how do we find all the integer sets [a1, a2, ... ,am] so that
a1 + a2 + ... + am = N
and with the constraint ai >= 1
For example if M = 4, and N = 7 there are three answers
[1,1,1,4]
[1,1,2,3]
[1,2,2,2]
Since you have to print all the sets that sum to N. You can employ a complete search algorithm using recursion. In the following code, M is the number of numbers in the set and N is the sum required.
int M;
int N;
void run(){
M = 4;
N = 7;
int[] arr = new int[M];
print(arr, 0, N, 1);
}
// req holds the required sum for the numbers in the array from arr[from]
// to arr[M-1].
// "last" holds the last value that we had put in the array.
// The first call to the array will be with last=1.
void print(int[] arr, int from, int req, int last){
// Reached the end of the array and sum required 0.
if(from==M && req==0){
System.out.println(Arrays.toString(arr));
return;
}
// Either reached the end of the array but sum is not equal to N
// Or if we have not reached the end of the array but sum has already
// become more than or equal to N.
if(from==M || req<=0){
return;
}
for(int i=last; i<=req; i++){
arr[from] = i;
print(arr, from+1, req-i, i);
}
}
Output for M=4 and N=7:
[1, 1, 1, 4]
[1, 1, 2, 3]
[1, 2, 2, 2]
Output for M=3 and N=10:
[1, 1, 8]
[1, 2, 7]
[1, 3, 6]
[1, 4, 5]
[2, 2, 6]
[2, 3, 5]
[2, 4, 4]
[3, 3, 4]
Answer for the problem in the link, just got accepted.
The idea is simple, assume that we know the maximum value for each section is X, and we want to find a way to divide these cabinets to achieve that , we can greedily divide them as follow:
Starting from first cabinet, iterating through each cabinet, until the total from first to ith cabinet is greater than X. So this is the first section, similarly, we can select other sections. This greedy will always find a solution (if exists).
Finally, we can using binary search to adjust the value of X, decrease X if we can find a way to divide the cabinets, or increase X if we cannot find one.
Here is the code in Java:
public class FairWorkload {
public int getMostWork(int[] folders, int workers) {
int[] data = new int[folders.length];
data[0] = folders[0];
for (int i = 1; i < data.length; i++) {
data[i] = data[i - 1] + folders[i];
}
if (workers == 1) {
return data[data.length - 1];
}
int start = 0;
int end = data[data.length - 1];
int result = Integer.MAX_VALUE;
while (start <= end) {
int mid = (start + end) / 2;
int index = 0;
for (int k = 0; k < workers && index < data.length; k++) {
int less = index > 0 ? data[index - 1] : 0;
int temp = index;
for (int i = index; i < data.length; i++) {
if (data[i] - less <= mid) {
temp = i;
} else {
break;
}
}
// System.out.println(data[temp] - less + " " + mid);
if(data[temp] - less > mid){
index = -1;
break;
}
index = temp + 1;
}
//System.out.println(mid + " " + index);
if (index != data.length) {
start = mid + 1;
} else {
result = Math.min(result, mid);
end = mid - 1;
}
}
return result;
}
public static void main(String[] args) {
int[] data = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1000};
System.out.println(new FairWorkload().getMostWork(data, 2));
}
}
One possible solution uses a counting technique, finding the rightmost term that satisfies [i] <= [length-1] - 2, and then flattening out all other terms to the right as much as possible, keeping a[i] <= a[i+1].
import java.util.Arrays;
public class test {
public static void main(String[] args) {
test t = new test();
t.go();
}
private void go() {
int w = 3;
int sum = 10;
int[] terms = new int[w];
for (int i = 0; i < terms.length; i++) {
terms[i] = 1;
}
terms[w-1] = sum - w + 1;
System.out.println(Arrays.toString(terms));
for (int i = right_index(terms); i>=0; i = right_index(terms)) {
terms[i]++;
int a = terms[i];
int overflow = -1;
// balance all the terms to the right
for (int j = i+1; j < terms.length-1; j++) {
overflow += terms[j] - a;
terms[j] = a;
}
terms[terms.length-1] += overflow;
System.out.println(Arrays.toString(terms));
}
}
// find the rightmost index i, where [i] <= [ia.length-1] - 2
private int right_index(int[] ia) {
int max = ia[ia.length-1];
for (int i = ia.length - 1; i >= 0; i--) {
if (ia[i] <= max - 2)
return i;
}
return -1;
}
}

Resources