I want to solve this CodeChef challenge:
Suppose We are given an array A of N(of range 100,000) elements. We are to find the count of all pairs of 3 such elements 1<=Ai,Aj,Ak<=30,000 such that
Aj-Ai = Ak- Aj and i < j < k
In other words Ai,Aj,Ak are in Arithmetic Progression. For instance for Array :
9 4 2 3 6 10 3 3 10
so The AP are:
{2,6,10},{9,6,3},{9,6,3},{3,3,3},{2,6,10}
So the required answer is 5.
My Approach
What I tried is take 30,000 long arrays named past and right. Initially right contains the count of each 1-30,000 element.
If we are at ith position past stores the count of array value before i and right stores the count of array after i. I simply loop for all possible common difference in the array. Here is the code :
right[arr[1]]--;
for(i=2;i<=n-1;i++)
{
past[arr[i-1]]++;
right[arr[i]]--;
k=30000 - arr[i];
if(arr[i] <= 15000)
k=arr[i];
for(d=1;d<=k;d++)
{
ans+= right[arr[i] + d]*past[arr[i]-d] + past[arr[i] + d]*right[arr[i]-d];
}
ans+=past[arr[i]]*right[arr[i]];
}
But this gets me Time Limit Exceeded. Please help with a better algorithm.
You can greatly cut execution time if you make a first pass over the list and only extract number pairs that it is possible to have an 3 term AP between (difference is 0 mod 2). And then iterating between such pairs.
Pseudo C++-y code:
// Contains information about each beginning point
struct BeginNode {
int value;
size_t offset;
SortedList<EndNode> ends; //sorted by EndNode.value
};
// Contains information about each class of end point
struct EndNode {
int value;
List<size_t> offsets; // will be sorted without effort due to how we collect offsets
};
struct Result {
size_t begin;
size_t middle;
size_t end;
};
SortedList<BeginNode> nodeList;
foreach (auto i : baseList) {
BeginNode begin;
node.value = i;
node.offset = i's offset; //you'll need to use old school for (i=0;etc;i++) with this
// baseList is the list between begin and end-2 (inclusive)
foreach (auto j : restList) {
// restList is the list between iterator i+2 and end (inclusive)
// we do not need to consider i+1, because not enough space for AP
if ((i-j)%2 == 0) { //if it's possible to have a 3 term AP between these two nodes
size_t listOffset = binarySearch(begin.ends);
if (listOffset is valid) {
begin.ends[listOffset].offsets.push_back(offsets);
} else {
EndNode end;
end.value = j;
end.offsets.push_back(j's offset);
begin.ends.sorted_insert(end);
}
}
}
if (begin has shit in it) {
nodeList.sorted_insert(begin);
}
}
// Collection done, now iterate over collection
List<Result> res;
foreach (auto node : nodeList) {
foreach (auto endNode : node.ends) {
foreach (value : sublist from node.offset until endNode.offsets.last()) {
if (value == average(node.value, endNode.value)) {
// binary_search here to determine how many offsets in "endNode.offsets" "value's offset" is less than.
do this that many times:
res.push_back({node.value, value, endNode.value});
}
}
}
}
return res;
Here's a simple C version of the solution that takes advantage of the Ai + Ak must be even test:
#include <stdio.h>
static int arr[] = {9, 4, 2, 3, 6, 10, 3, 3, 10};
int main ()
{
int i, j, k;
int sz = sizeof(arr)/sizeof(arr[0]);
int count = 0;
for (i = 0; i < sz - 2; i++)
{
for (k = i + 2; k < sz; k++)
{
int ik = arr[i] + arr[k];
int ikdb2 = ik / 2;
if ((ikdb2 * 2) == ik) // if ik is even
{
for (j = i + 1; j < k; j++)
{
if (arr[j] == ikdb2)
{
count += 1;
printf("{%d, %d, %d}\n", arr[i], arr[j], arr[k]);
}
}
}
}
}
printf("Count is: %d\n", count);
}
and the console dribble:
tmp e$ cc -o triples triples.c
tmp e$ ./triples
{9, 6, 3}
{9, 6, 3}
{2, 6, 10}
{2, 6, 10}
{3, 3, 3}
Count is: 5
tmp e$
This more complicated version keeps a list of Aj indexed by value to go from n-cubed to n-squared (kinda).
#include <stdio.h>
#include <stdint.h>
static uint32_t arr[] = {9, 4, 2, 3, 6, 10, 3, 3, 10};
#define MAX_VALUE 100000u
#define MAX_ASIZE 30000u
static uint16_t index[MAX_VALUE+1];
static uint16_t list[MAX_ASIZE+1];
static inline void remove_from_index (int subscript)
{
list[subscript] = 0u; // it is guaranteed to be the last element
uint32_t value = arr[subscript];
if (value <= MAX_VALUE && subscript == index[value])
{
index[value] = 0u; // list now empty
}
}
static inline void add_to_index (int subscript)
{
uint32_t value = arr[subscript];
if (value <= MAX_VALUE)
{
list[subscript] = index[value]; // cons
index[value] = subscript;
}
}
int main ()
{
int i, k;
int sz = sizeof(arr)/sizeof(arr[0]);
int count = 0;
for (i = 0; i < sz - 2; i++)
{
for (k = i; k < sz; k++) remove_from_index(k);
for (k = i + 2; k < sz; k++)
{
uint32_t ik = arr[i] + arr[k];
uint32_t ikdb2 = ik / 2;
add_to_index(k-1); // A(k-1) is now a legal middle value
if ((ikdb2 * 2) == ik) // if ik is even
{
uint16_t rover = index[ikdb2];
while (rover != 0u)
{
count += 1;
printf("{%d, %d, %d}\n", arr[i], arr[rover], arr[k]);
rover = list[rover];
}
}
}
}
printf("Count is: %d\n", count);
}
and the dribble:
tmp e$ cc -o triples triples.c
tmp e$ ./triples
{9, 6, 3}
{9, 6, 3}
{2, 6, 10}
{2, 6, 10}
{3, 3, 3}
Count is: 5
tmp e$
Related
I wanted to ask how to check if a group of numbers could be split into subgroups (every subgroup has to have 3 members) that every sum of subgroups' members would be equal. How to check so many combinations?
Example:
int numbers[] = {1, 2, 5, 6, 8, 3, 2, 4, 5};
can be divided into
{1, 5, 6}, {2, 8, 2}, {3, 4, 5}
A recursive approach can be followed, where one keeps two arrays:
An array with the sums of every subgroup.
A boolean array to check whether an element is already taken into
some subgroup or not.
You asked for 3 subgroups, i.e. K = 3 in the rest of this post, but keep in mind that when dealing with recursion, bases cases should be taken into account. In this case we will focus on two base cases:
If K is 1, then we already have our answer, complete array is only
subset with same sum.
If N < K, then it is not possible to divide array into subsets with
equal sum, because we can’t divide the array into more than N parts.
If the sum of group is not divisible by K, then it is not possible to divide it. We will only proceed if k divides sum. Our goal reduces to divide the group into K subgroups where sum of each subgroup should be the sum of the group divided by K.
In the code below a recursive method is written which tries to add array element into some subset. If sum of this subset reaches required sum, we iterate for next part recursively, otherwise we backtrack for different set of elements. If number of subsets whose sum reaches the required sum is (K-1), we flag that it is possible to partition array into K parts with equal sum, because remaining elements already have a sum equal to required sum.
Quoted from here, while in your case you would set K = 3, as in the example code.
// C++ program to check whether an array can be
// subsetitioned into K subsets of equal sum
#include <bits/stdc++.h>
using namespace std;
// Recursive Utility method to check K equal sum
// subsetition of array
/**
array - given input array
subsetSum array - sum to store each subset of the array
taken - boolean array to check whether element
is taken into sum subsetition or not
K - number of subsetitions needed
N - total number of element in array
curIdx - current subsetSum index
limitIdx - lastIdx from where array element should
be taken */
bool isKPartitionPossibleRec(int arr[], int subsetSum[], bool taken[],
int subset, int K, int N, int curIdx, int limitIdx)
{
if (subsetSum[curIdx] == subset)
{
/* current index (K - 2) represents (K - 1) subsets of equal
sum last subsetition will already remain with sum 'subset'*/
if (curIdx == K - 2)
return true;
// recursive call for next subsetition
return isKPartitionPossibleRec(arr, subsetSum, taken, subset,
K, N, curIdx + 1, N - 1);
}
// start from limitIdx and include elements into current subsetition
for (int i = limitIdx; i >= 0; i--)
{
// if already taken, continue
if (taken[i])
continue;
int tmp = subsetSum[curIdx] + arr[i];
// if temp is less than subset then only include the element
// and call recursively
if (tmp <= subset)
{
// mark the element and include into current subsetition sum
taken[i] = true;
subsetSum[curIdx] += arr[i];
bool nxt = isKPartitionPossibleRec(arr, subsetSum, taken,
subset, K, N, curIdx, i - 1);
// after recursive call unmark the element and remove from
// subsetition sum
taken[i] = false;
subsetSum[curIdx] -= arr[i];
if (nxt)
return true;
}
}
return false;
}
// Method returns true if arr can be subsetitioned into K subsets
// with equal sum
bool isKPartitionPossible(int arr[], int N, int K)
{
// If K is 1, then complete array will be our answer
if (K == 1)
return true;
// If total number of subsetitions are more than N, then
// division is not possible
if (N < K)
return false;
// if array sum is not divisible by K then we can't divide
// array into K subsetitions
int sum = 0;
for (int i = 0; i < N; i++)
sum += arr[i];
if (sum % K != 0)
return false;
// the sum of each subset should be subset (= sum / K)
int subset = sum / K;
int subsetSum[K];
bool taken[N];
// Initialize sum of each subset from 0
for (int i = 0; i < K; i++)
subsetSum[i] = 0;
// mark all elements as not taken
for (int i = 0; i < N; i++)
taken[i] = false;
// initialize first subsubset sum as last element of
// array and mark that as taken
subsetSum[0] = arr[N - 1];
taken[N - 1] = true;
if (subset < subsetSum[0])
return false;
// call recursive method to check K-subsetition condition
return isKPartitionPossibleRec(arr, subsetSum, taken,
subset, K, N, 0, N - 1);
}
// Driver code to test above methods
int main()
{
int arr[] = {2, 1, 4, 5, 3, 3};
int N = sizeof(arr) / sizeof(arr[0]);
int K = 3;
if (isKPartitionPossible(arr, N, K))
cout << "Partitions into equal sum is possible.\n";
else
cout << "Partitions into equal sum is not possible.\n";
}
Output:
Partitions into equal sum is possible.
Relevant links: 2 and 3.
You could just do something like that in this particular case (3x3):
const int COUNT = 9;
bool test(int const (&array)[COUNT], std::vector<std::vector<int>>* result) {
for(int _1=0; _1<COUNT-2; ++_1) {
for(int _2=1; _2<COUNT-1; ++_2) {
if(_2 == _1)
continue;
for(int _3=2; _3<COUNT; ++_3) {
if(_3 == _2 || _3 == _1)
continue;
std::vector<int> chosen1 {array[_1], array[_2], array[_3]};
std::vector<int> rest;
for(int _x = 0; _x < COUNT; ++_x) {
if(_x != _1 && _x != _2 && _x != _3) {
rest.push_back(array[_x]);
}
}
for (int _4 = 0; _4 < COUNT-5; ++_4) {
for (int _5 = 1; _5 < COUNT-4; ++_5) {
if(_5 == _4)
continue;
for (int _6 = 2; _6 < COUNT-3; ++_6) {
if(_6 == _5 || _6 == _4)
continue;
std::vector<int> chosen2 = {rest[_4], rest[_5], rest[_6]};
std::vector<int> chosen3;
for(int _x = 0; _x < COUNT-3; ++_x) {
if(_x != _4 && _x != _5 && _x != _6) {
chosen3.push_back(rest[_x]);
}
}
int total = std::accumulate(chosen1.begin(), chosen1.end(), 0);
if((std::accumulate(chosen2.begin(), chosen2.end(), 0) == total) &&
(std::accumulate(chosen3.begin(), chosen3.end(), 0) == total)) {
*result = {chosen1, chosen2, chosen3};
return true;
}
}
}
}
}
}
}
return false;
}
int main() {
int values[] = {1, 2, 5, 6, 8, 3, 2, 4, 5};
std::vector<std::vector<int>> result;
if(test(values, &result)) {
for(auto& x : result) {
std::cout << "{";
for(auto& y : x) {
std::cout << y << ",";
}
std::cout << "}";
}
std::cout << std::endl;
} else {
std::cout << "not found";
}
}
If you had longer array (3+ * 3) then you could use recurrence (you could use it in my example too), but that would be still very slow.
I have been trying to come out with a solution regarding the problem of finding the last digit of the sum of large n Fibonacci series.
I have been able to pass several test cases with large n. But I'm stuck at the following case where n = 832564823476. I know it can be solved using Pisano's period but I'm unable to come out with a efficient algo. Any help would be great. Thanks.
My code that I have implemented is as follows-
#include <iostream>
using namespace std;
int calc_fib(int n) {
int fib[n+1];
fib[0]=0;
fib[1]=1;
int res = 1;
for(int i = 2; i<=n;i++){
fib[i] = (fib[i-1]%10 + fib[i-2]%10)%10;
res = res + fib[i];
}
return (res%10);
}
int main() {
int n = 0;
std::cin >> n;
std::cout << calc_fib(n) << '\n';
return 0;
}
SOLVED IT
Works on all range of inputs. It works on the following algorithm.
The idea is to notice that the last digits of fibonacci numbers also occur in sequences of length 60 (from the previous problem: since pisano peiod of 10 is 60). Irrespective of how large n is, its last digit is going to have appeared somewhere within the sequence.
Two Things apart from edge case of 10 as last digit.
Sum of nth Fibonacci series = F(n+2) -1
Then pisano period of module 10 = let n+2 mod (60) = m then find F(m) mod(10)-1
Code as follows;
#include <iostream>
using namespace std;
long long calc_fib(long long n) {
n = (n+2)%60;
int fib[n+1];
fib[0]=0;
fib[1]=1;
int res = 1;
for(int i = 2; i<=n;i++){
fib[i] = (fib[i-1]%10 + fib[i-2]%10)%10;
// res = res + fib[i];
}
// cout<<fib[n]<<"\n";
if(fib[n] == 0){
return 9;
}
return (fib[n]%10-1);
}
int main() {
long long n = 0;
std::cin >> n;
std::cout << calc_fib(n) << '\n';
return 0;
}
Actually it's even easier than Niall answer
int get_fibonacci_sum_last_digit(long long n) {
const int kPisanoSize = 60;
int rest = n % kPisanoSize;
int preparedNumbers[kPisanoSize] = {0, 1, 2, 4, 7, 2, 0, 3, 4, 8, 3,
2, 6, 9, 6, 6, 3, 0, 4, 5, 0, 6, 7, 4, 2, 7, 0, 8, 9, 8, 8, 7,
6, 4, 1, 6, 8, 5, 4, 0, 5, 6, 2, 9, 2, 2, 5, 8, 4, 3, 8, 2, 1,
4, 6, 1, 8, 0, 9, 0};
return preparedNumbers[rest];
}
If you only need to output the last digit as you said, I think you can just make use of the Pisano Period you mentioned, as for modular 10, the cycle length is only 60 and you can just pre-make an array of that 60 digits.
If you want to compute by yourself, I think you can use Matrix Exponentiation which gives you O(lg N) complexity, when calculating the matrix exponents, keep storing the temporary result modular 10. See the Matrices section for your reference.
For your function removing the array.
#include "stdafx.h"
#include <iostream>
using namespace std;
int calc_fib(long long int n) {
int fibzero = 0;
int fibone = 1;
int fibnext;
long long int res = 1;
for (long long int i = 2; i <= n; i++) {
fibnext = (fibone + fibzero) % 10;
fibzero = fibone;
fibone = fibnext;
res = res + fibnext;
}
return (res % 10);
}
int main()
{
long long int n = 0;
std::cin >> n;
std::cout << calc_fib(n) << '\n';
return 0;
}
Last digit of Fibonacci sum repeats after 60 elements.
Here the period is 60 [0-59].
So to get the last digit of n'th sum of number is the last digit of n%60'th sum of
number
#include <iostream>
#include <vector>
#include <algorithm>
int get_last_digit(int n){
std::vector<int> last_digits(60);
long long a = 0, b = 1;
last_digits[0] = 0;
last_digits[1] = 1;
long long temp, sum = 1;
// Fill last_digits vector with the first 60 sums last digits
for (int i = 2; i < 60; i++) {
temp = a+b;
a = b;
b = temp;
sum += temp;
last_digits[i] = sum%10;
}
// Now return n%60'th element
return last_digits[n%60];
}
int main(int argc, char const *argv[]) {
int n;
std::cin>>n;
std::cout << get_last_digit(n);
return 0;
}
A neat way to solve the problem (I use java). The logic is to utilize the pisano period of 10. To write down the corresponding relationship between Sum(F(n)) = F(n+2) + 1 can help a lot. tip: create a fibonacci sequence alone.
private static long getFibonacciSum(long n) {
n = (n + 2) % 60;
long[] fib = new long[(int) n + 1];
long cor;
fib[0] = 0;
fib[1] = 1;
for (int i = 2; i <= n; i++) {
fib[i] = (fib[i - 1] + fib[i - 2]) % 10;
}
if (fib[(int) n] == 0) cor = 9;
else cor = fib[(int) n] - 1;
return cor;
}
The task is to rotate left or rotate right a subarray of an array given number of times.
Let me explain this on an example:
lets data be an array.
data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
a sub array is determined by parameters begin and end.
if begin = 3 and end = 7, then subarray is {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
if begin = 7 and end = 3, then subarray is {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
let's rotate it right two times
if begin = 3 and end = 7, then the result is {0, 1, 2, 6, 7, 3, 4, 5, 8, 9};
if begin = 7 and end = 3, then the result is {8, 9, 0, 1,, 4, 5, 6, 2, 3, 7};
I've written code that performs this task but it's to slow.
Can someone give me a hint how to make it quicker?
Important: I'm not allowed to use other arrays than data, subprograms and build-in functions.
#include <iostream>
using namespace std;
int main(){
int dataLength;
cin >> dataLength;
int data [ dataLength ];
for (int i = 0; i < dataLength; i++){
cin >> data [ i ];
}
int begin;
int end;
int rotation;
int forLoopLength;
int tempBefore;
int tempAfter;
cin >> begin;
cin >> end;
cin >> rotation;
if (end > begin)
forLoopLength = (end - begin) + 1;
else
forLoopLength = (end - begin) + 1 + dataLength;
if (rotation < 0)
rotation = forLoopLength + (rotation % forLoopLength);
else
rotation = rotation % forLoopLength;
for (int i = 0; i < rotation; i++) {
tempBefore = data [ end ];
for (int i = 0; i < forLoopLength; i++) {
tempAfter = data [ (begin + i) % dataLength ];
data [ (begin + i) % dataLength ] = tempBefore;
tempBefore = tempAfter;
}
}
for (int i = 0; i < dataLength; i ++ ) {
cout << data [ i ] << " ";
}
return 0;
}
There's a trick to this. It's pretty weird that you'd get this for homework if the trick wasn't mentioned in class. Anyway...
To rotate a sequence of N elements left by M:
reverse the whole sequence
reverse the last M elements
reverse the first N-M elements
done
e.g. left by 2:
1234567
->
7654321
->
7654312
->
3456712
Here is my code, it makes exactly n reads and n writes, where n is subarray size.
#include<iostream>
int arr[]= { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// replacing 'addr( pos, from, size )' with just 'pos' mean rotation the whole array
int addr( int ptr, int from, int size)
{
return (ptr + from ) % size;
}
void rotate( int* arr, int shift, int from, int count, int size)
{
int i;
int pos= 0;
int cycle= 0;
int c= 0;
int c_old= 0;
// exactly count steps
for ( i=0; i< count; i++ ){
// rotation of arrays part is essentially a permutation.
// every permutation can be decomposed on cycles
// here cycle processing begins
c= arr[ addr( pos, from, size ) ];
while (1){
// one step inside the cycle
pos= (pos + shift) % count;
if ( pos == cycle )
break;
c_old= c;
c= arr[ addr( pos, from, size ) ];
arr[ addr( pos, from, size ) ]= c_old;
i++;
}
// here cycle processing ends
arr[ addr( pos, from, size ) ]= c;
pos= (pos + 1) % count;
cycle= (cycle + 1) % count;
}
}
int main()
{
rotate( arr, 4, 6, 6, 11 );
int i;
for ( i=0; i<11; i++){
std::cout << arr[i] << " ";
}
std::cout << std::endl;
return 0;
}
Suppose the array is 1 2 3 4 5
Here N = 5 and we have to select 3 elements and we cannot select more than 2 consecutive elements, so P = 3 and k = 2. So the output here will be 1 + 2 + 4 = 7.
I came up with a recursive solution, but it has an exponential time complexity. Here is the code.
#include<iostream>
using namespace std;
void mincost_hoarding (int *arr, int max_size, int P, int k, int iter, int& min_val, int sum_sofar, int orig_k)
{
if (P == 0)
{
if (sum_sofar < min_val)
min_val = sum_sofar;
return;
}
if (iter == max_size)
return;
if (k!=0)
{
mincost_hoarding (arr, max_size, P - 1, k - 1, iter + 1, min_val, sum_sofar + arr[iter], orig_k);
mincost_hoarding (arr, max_size, P, orig_k, iter + 1, min_val, sum_sofar, orig_k);
}
else
{
mincost_hoarding (arr, max_size, P, orig_k, iter + 1, min_val, sum_sofar, orig_k);
}
}
int main()
{
int a[] = {10, 5, 13, 8, 2, 11, 6, 4};
int N = sizeof(a)/sizeof(a[0]);
int P = 2;
int k = 1;
int min_val = INT_MAX;
mincost_hoarding (a, N, P, k, 0, min_val, 0, k);
cout<<min_val;
}
Also, if supposedly P elements cannot be selected following the constraint, then we return INT_MAX.
I was asked this question in an interview. After proposing this solution, the interviewer was expecting something faster. Maybe, a DP approach towards the problem. Can someone propose a DP algorithm if there exists one, or a faster algorithm.
I have tried various tests cases and got correct answers. If you find some test cases that are giving incorrect response, please point that out too.
Below is a Java Dynamic Programming algorithm.
(the C++ version should look very similar)
It basically works as follows:
Have a 3D array of [pos][consecutive length][length]
Here length index = actual length - 1), so [0] would be length 1, similarly for consecutive length. This was done since there's no point to having length 0 anywhere.
At every position:
If at length 0 and consecutive length 0, just use the value at pos.
Otherwise, if consecutive length 0, look around for the minimum in all the previous positions (except pos - 1) with length - 1 and use that plus the value at pos.
For everything else, if pos > 0 && consecutive length > 0 && length > 0,
use [pos-1][consecutive length-1][length-1] plus the value at pos.
If one of those are 0, initialize it to an invalid value.
Initially it felt like one only needs 2 dimensions for this problem, however, as soon as I tried to figure it out, I realized I needed a 3rd.
Code:
int[] arr = {1, 2, 3, 4, 5};
int k = 2, P = 3;
int[][][] A = new int[arr.length][P][k];
for (int pos = 0; pos < arr.length; pos++)
for (int len = 0; len < P; len++)
{
int min = 1000000;
if (len > 0)
{
for (int pos2 = 0; pos2 < pos-1; pos2++)
for (int con = 0; con < k; con++)
min = Math.min(min, A[pos2][len-1][con]);
A[pos][len][0] = min + arr[pos];
}
else
A[pos][0][0] = arr[pos];
for (int con = 1; con < k; con++)
if (pos > 0 && len > 0)
A[pos][len][con] = A[pos-1][len-1][con-1] + arr[pos];
else
A[pos][len][con] = 1000000;
}
// Determine the minimum sum
int min = 100000;
for (int pos = 0; pos < arr.length; pos++)
for (int con = 0; con < k; con++)
min = Math.min(A[pos][P-1][con], min);
System.out.println(min);
Here we get 7 as output, as expected.
Running time: O(N2k + NPk)
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
Let's say I have N chocolates that have to be packed into exactly P boxes in the order they arrive. Each chocolate also has a number of calories X and each box has a capacity K which has to be less than or equal to 3*sum(x1, x2, ..., xn) + max(x1, x2, ..., xn)^2 - min(x1, x2, ..., xn)^2.
In the task I'm given N, P and X for each chocolate and I have to figure out the lowest possible K. Could anyone help me on this (not looking for a solution just for some hints regarding the problem)?
Example:
N = 8,
P = 3,
X = {1, 4, 5, 6, 3, 2, 5, 3}
K for first three chocolates = 3*(1+4+5) + 5^2 - 1^2 = 54
K for next two chocolates = 3*(6+3) + 6^2 - 3^2 = 54
K for last three chocolates = 3*(2+5+3) + 5^2 - 2^2 = 51
Lowest possible K = 54
So the goal is to find the best combination using exactly P boxes that has the lowest K.
Thanks!
Here is how I would solve this in Java:
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class ChocolatePuzzle {
private static final Map <String, Integer> solutions =
new HashMap <String, Integer> ();
private static final Map <String, Integer> bestMoves =
new HashMap <String, Integer> ();
private static int [] x;
private static int k (int from, int to)
{
int sum = x [from];
int max = x [from];
int min = x [from];
for (int i = from + 1; i < to; i++)
{
sum += x [i];
max = Math.max (max, x [i]);
min = Math.min (min, x [i]);
}
return sum * 3 + max * max - min * min;
}
public static int solve (int n, int p)
{
String signature = n + "," + p;
Integer solution = solutions.get (signature);
if (solution == null)
{
solution = Integer.valueOf (doSolve (n, p, signature));
solutions.put (signature, solution);
}
return solution.intValue ();
}
public static int doSolve (int n, int p, String signature)
{
if (p == 1)
{
bestMoves.put (signature, Integer.valueOf (x.length - n));
return k (n, x.length);
}
else
{
int result = Integer.MAX_VALUE;
int bestMove = 0;
int maxI = x.length - n - p + 1;
for (int i = 1; i <= maxI; i++)
{
int k = Math.max (k (n, n + i), solve (n + i, p - 1));
if (k < result)
{
result = k;
bestMove = i;
}
}
bestMoves.put (signature, Integer.valueOf (bestMove));
return result;
}
}
public static void main(String[] args) {
int n = 20;
int p = 5;
x = new int [n];
Random r = new Random ();
for (int i = 0; i < n; i++)
x [i] = r.nextInt (9) + 1;
System.out.println("N: " + n);
System.out.println("P: " + p);
System.out.print("X: {");
for (int i = 0; i < n; i++)
{
if (i > 0) System.out.print (", ");
System.out.print (x [i]);
}
System.out.println("}");
System.out.println();
int k = solve (0, p);
int o = 0;
for (int i = p; i > 0; i--)
{
int m = bestMoves.get (o + "," + i);
System.out.print ("{");
for (int j = 0; j < m; j++)
{
if (j > 0)
System.out.print (", ");
System.out.print (x [o + j]);
}
System.out.print ("} (k: ");
System.out.print(k (o, o + m));
System.out.println (")");
o += m;
}
System.out.println("min(k): " + k);
}
}
Probably you could find some useful tips in this code.
Sample input:
N: 20
P: 5
X: {1, 7, 6, 6, 5, 5, 7, 9, 1, 3, 9, 5, 3, 7, 9, 1, 4, 2, 4, 8}
Sample output:
{1, 7, 6, 6} (k: 108)
{5, 5, 7, 9} (k: 134)
{1, 3, 9, 5} (k: 134)
{3, 7, 9} (k: 129)
{1, 4, 2, 4, 8} (k: 120)
min(k): 134