Choose n unique elements from n lists - algorithm

I have been thinking about a programing problem. If we have n lists, we want to output n diffrent elements (each one from a different list). I suspect this can be solved with some kind of a backtrack algorithm but I don't see how to correctly implement it.

Though you could solve this with backtracking as suggested in a comment, a more efficient solution would be to use a max-flow algorithm.
Model it as a graph. A Source, a Sink a node for each distinct element and a node for each list.
You have the source connected to each distinct element. Each element connected to every list it is in. and the lists connected to a sink node. Each edge with capacity 1.
The maximum flow is the maximum number of distinct elements from distinct lists you can select.
https://en.wikipedia.org/wiki/Maximum_flow_problem
https://en.wikipedia.org/wiki/Edmonds%E2%80%93Karp_algorithm

I'm not exactly sure that this has no bugs and solves what is being asked; and I hope I have understood the question right this time :)
//test
int[][] arr = new int[][] {
new int[] { 1, 1, 1, 1, 6 },
new int[] { 1, 1, 1, 1, 5 },
new int[] { 1, 1, 1, 4, 9 },
new int[] { 1, 1, 3, 8, 1 },
new int[] { 1, 2, 7, 1, 1 },
new int[] { 1, 1, 1, 1, 1 } };
int[] res = getItems(arr).ToArray(); //6,5,4,3,2,1
private static IEnumerable<T> getItems<T>(T[][] array)
{
int N = array.GetLength(0);
item<T>[] items = new item<T>[N];
HashSet<T> hs = new HashSet<T>();
for (int i = 0; i < N; i++)
{
bool ok = false;
T[] arr = array[i];
for (int j = items[i].Index; j < arr.Length; j++)
{
T val = arr[j];
if (hs.Add(val))
{
items[i] = new item<T>() { Val = val, Index = j };
ok = true;
break;
}
}
if (!ok)
{
item<T> item;
do
{
if (i == 0) throw new Exception("no solution");
items[i] = new item<T>();
item = items[--i];
item.Index++;
items[i] = item;
}
while (item.Index >= array[i].Length);
hs.Clear();
for (int j = 0; j < i; j++)
hs.Add(array[j][items[j].Index]);
i--;
}
}
return hs;
}
private struct item<T>
{
public T Val;
public int Index;
public override string ToString()
{
return string.Format("Val:{0} Index:{1}", Val, Index);
}
}

Related

Algorithm to find the best combination of a list if given sets

I'm looking for an algorithm to find the best combination (the highest weight) of a list of sets. For example, let's say we have items 'A', 'B' and 'C' and we got 4 A's, 2 B's and 3 C's. Some possible combinations could be:
{A,B,C},{A,B,C},{A,C},{A}
{A,B},{A,B,C},{A,C},{A,C}
Then the weights are based on the number of items per set, for example:
1 item: 5
2 items: 15
3 items: 20
So in this case the first combination would have a weight of 20+20+15+5=60 and the second one would be 15+20+15+15=65. A greedy algorithm wouldn't work in this case because there are cases where looking for the highest number of items does not return the best combination.
Any ideas?
I solved this using recursion.
These variables define the problem (static variables):
private static boolean[] BEST_SOLUTION_SO_FAR = null;
private static int BEST_WEIGHT_SO_FAR = 0;
private static String[][] POSSIBLE_SETS = {{"A","B","C"},{"A","B","C"},{"A","C"},{"A"},{"A","B"},{"A","B","C"},{"A","C"},{"A","C"}};
private static Map<String, Integer> MAX_OF_EACH_ITEM = new HashMap<>();
static {
MAX_OF_EACH_ITEM.put("A", 4);
MAX_OF_EACH_ITEM.put("B", 2);
MAX_OF_EACH_ITEM.put("C", 3);
}
This is the main method (which sets everything up for the start of the recursion):
public static void main(String[] args) {
BEST_WEIGHT_SO_FAR = 0;
BEST_SOLUTION_SO_FAR = null;
// start the recursion
buildSolution(new boolean[POSSIBLE_SETS.length], new HashMap<>());
// print solution
System.out.println("BEST SOLUTION : ");
for (int i = 0; i < POSSIBLE_SETS.length; i++) {
if(BEST_SOLUTION_SO_FAR[i]){
System.out.println(Arrays.toString(POSSIBLE_SETS[i]));
}
}
}
And this is the recursive method:
private static void buildSolution(boolean[] isSelected, Map<String, Integer> itemsUsed){
boolean canBeExpanded = false;
for (int i = 0; i < isSelected.length; i++) {
// check whether another set can be added
if(!isSelected[i]){
// calculate new numbers of items used
Map<String, Integer> itemsUsedB = new HashMap<>(itemsUsed);
for (int j = 0; j < POSSIBLE_SETS[i].length; j++) {
String key = POSSIBLE_SETS[i][j];
if(itemsUsedB.containsKey(key))
itemsUsedB.put(key, itemsUsedB.get(key) + 1);
else
itemsUsedB.put(key, 1);
}
// check whether this is possible
boolean isPossible = true;
for(String key : MAX_OF_EACH_ITEM.keySet()){
if(itemsUsedB.containsKey(key) && itemsUsedB.get(key) > MAX_OF_EACH_ITEM.get(key)){
isPossible = false;
break;
}
}
// if not possible attempt next
if(!isPossible)
continue;
// mark this solution as expandable
canBeExpanded = true;
// mark as selected
isSelected[i] = true;
// recurse
buildSolution(isSelected, itemsUsedB);
// undo mark
isSelected[i] = false;
}
}
// a solution that can no longer be expanded was found
if(!canBeExpanded){
int w = 0;
int[] setSizeWeight = {0,5,15,20};
for (int i = 0; i < isSelected.length; i++) {
w += isSelected[i] ? setSizeWeight[POSSIBLE_SETS[i].length] : 0;
}
if(w > BEST_WEIGHT_SO_FAR){
BEST_WEIGHT_SO_FAR = w;
BEST_SOLUTION_SO_FAR = Arrays.copyOf(isSelected, isSelected.length);
}
}
}
It outputs:
BEST SOLUTION
[A, B, C]
[A, C]
[A, B]
[A, C]
Which has a weight of 65.

Heap sort not working as expected

I'm trying to implement heap sort using the CLRS book. My code is:
private void maxHeapify(int[] input, int i) {
int l = left(i);
int r = right(i);
int largest;
if(l <= heapSize && input[l] > input[i]) {
largest = l;
} else {
largest = i;
}
if(r <= heapSize && input[r] > input[largest]) {
largest = r;
}
if(largest != i) {
swap(input, i, largest);
maxHeapify(input, largest);
}
}
private void buildMaxHeap(int[] input) {
heapSize = input.length;
for(int i = (input.length-1)/2; i >= 0; i--) {
maxHeapify(input, i);
}
}
public void heapSort(int[] input) {
buildMaxHeap(input);
for(int i = input.length-1; i > 0; i--) {
swap(input, 0, i);
heapSize--;
maxHeapify(input, 0);
}
}
For an input of {1, 5, 3, 7, 2, 0, 6, 2}, I'm getting the answer as 7 3 0 6 2 5 1 2. Why is this happening? I'm guessing it has something to do with the line for(int i = (input.length-1)/2; i >= 0; i--) but I can't put my finger on it.

Puzzle: Find the order of n persons standing in a line (based on their heights)

Saw this question on Careercup.com:
Given heights of n persons standing in a line and a list of numbers corresponding to each person (p) that gives the number of persons who are taller than p and standing in front of p. For example,
Heights: 5 3 2 6 1 4
InFronts:0 1 2 0 3 2
Means that the actual actual order is: 5 3 2 1 6 4
The question gets the two lists of Heights and InFronts, and should generate the order standing in line.
My solution:
It could be solved by first sorting the list in descending order. Obviously, to sort, we need to define an object Person (with two attributes of Height and InFront) and then sort Persons based on their height. Then, I would use two stacks, a main stack and a temp one, to build up the order.
Starting from the tallest, put it in the main stack. If the next person had an InFront value of greater than the person on top of the stack, that means the new person should be added before the person on top. Therefore, we need to pop persons from the main stack, insert the new person, and then return the persons popped out in the first step (back to the main stack from temp one). I would use a temp stack to keep the order of the popped out persons. But how many should be popped out? Since the list is sorted, we need to pop exactly the number of persons in front of the new person, i.e. corresponding InFront.
I think this solution works. But the worst case order would be O(n^2) -- when putting a person in place needs popping out all previous ones.
Is there any other solutions? possibly in O(n)?
The O(nlogn) algoritm is possible.
First assume that all heights are different.
Sort people by heights. Then iterate from shortest to tallest. In each step you need an efficient way to put the next person to the correct position. Notice that people we've already placed are not taller that the current person. And the people we place after are taller than the current. So we have to find a place such that the number of empty positions in the front is equal to the inFronts value of this person. This task can be done using a data structure called interval tree in O(logn) time. So the total time of an algorithm is O(nlogn).
This algorithm works well in case where there's no ties. As it may be safely assumed that empty places up to front will be filled by taller people.
In case when ties are possible, we need to assure that people of the same height are placed in increasing order of their positions. It can be achieved if we will process people by non-decreasing inFronts value. So, in case of possible ties we should also consider inFronts values when sorting people.
And if at some step we can't find a position for next person then the answer it "it's impossible to satisfy problem constraints".
There exists an algorithm with O(nlogn) average complexity, however worst case complexity is still O(n²).
To achieve this you can use a variation of a binary tree. The idea is, in this tree, each node corresponds to a person and each node keeps track of how many people are in front of him (which is the size of the left subtree) as nodes are inserted.
Start iterating the persons array in decreasing height order and insert each person into the tree starting from the root. Insertion is as follows:
Compare the frontCount of the person with the current node's (root at the beginning) value.
If it is smaller than it insert the node to the left with value 1. Increase the current node's value by 1.
Else, descend to the right by decreasing the person's frontCount by current node's value. This enables the node to be placed in the correct location.
After all nodes finished, an inorder traversal gives the correct order of people.
Let the code speak for itself:
public static void arrange(int[] heights, int[] frontCounts) {
Person[] persons = new Person[heights.length];
for (int i = 0; i < persons.length; i++)
persons[i] = new Person(heights[i], frontCounts[i]);
Arrays.sort(persons, (p1, p2) -> {
return Integer.compare(p2.height, p1.height);
});
Node root = new Node(persons[0]);
for (int i = 1; i < persons.length; i++) {
insert(root, persons[i]);
}
inOrderPrint(root);
}
private static void insert(Node root, Person p) {
insert(root, p, p.frontCount);
}
private static void insert(Node root, Person p, int value) {
if (value < root.value) { // should insert to the left
if (root.left == null) {
root.left = new Node(p);
} else {
insert(root.left, p, value);
}
root.value++; // Increase the current node value while descending left!
} else { // insert to the right
if (root.right == null) {
root.right = new Node(p);
} else {
insert(root.right, p, value - root.value);
}
}
}
private static void inOrderPrint(Node root) {
if (root == null)
return;
inOrderPrint(root.left);
System.out.print(root.person.height);
inOrderPrint(root.right);
}
private static class Node {
Node left, right;
int value;
public final Person person;
public Node(Person person) {
this.value = 1;
this.person = person;
}
}
private static class Person {
public final int height;
public final int frontCount;
Person(int height, int frontCount) {
this.height = height;
this.frontCount = frontCount;
}
}
public static void main(String[] args) {
int[] heights = {5, 3, 2, 6, 1, 4};
int[] frontCounts = {0, 1, 2, 0, 3, 2};
arrange(heights, frontCounts);
}
I think one approach can be the following. Although it again seems to be O(n^2) at present.
Sort the Height array and corresponding 'p' array in ascending order of heights (in O(nlogn)). Pick the first element in the list. Put that element in the final array in the position given by the p index.
For example after sorting,
H - 1, 2, 3, 4, 5, 6
p - 3, 2, 1, 2, 0, 0.
1st element should go in position 3. Hence final array becomes:
---1--
2nd element shall go in position 2. Hence final array becomes:
--21--
3rd element should go in position 1. Hence final array becomes:
-321--
4th element shall go in position 2. This is the position among the empty ones. Hence final array becomes:
-321-4
5th element shall go in position 0. Hence final array becomes:
5321-4
6th element should go in position 0. Hence final array becomes:
532164
I think the approach indicated above is correct. However a critical piece missing in the solutions above are.
Infronts is the number of taller candidate before the current person. So after sorting the persons based on height(Ascending), when placing person 3 with infront=2, if person 1 and 2 was in front placed at 0, 1 position respectively, you need to discount their position and place 3 at position 4, I.E 2 taller candidates will take position 2,3.
As some indicated interval tree is the right structure. However a dynamic sized container, with available position will do the job.(code below)
struct Person{
int h, ct;
Person(int ht, int c){
h = ht;
ct = c;
}
};
struct comp{
bool operator()(const Person& lhs, const Person& rhs){
return (lhs.h < rhs.h);
}
};
vector<int> heightOrder(vector<int> &heights, vector<int> &infronts) {
if(heights.size() != infronts.size()){
return {};
}
vector<int> result(infronts.size(), -1);
vector<Person> persons;
vector<int> countSet;
for(int i= 0; i< heights.size(); i++){
persons.emplace_back(Person(heights[i], infronts[i]));
countSet.emplace_back(i);
}
sort(persons.begin(), persons.end(), comp());
for(size_t i=0; i<persons.size(); i++){
Person p = persons[i];
if(countSet.size() > p.ct){
int curr = countSet[p.ct];
//cout << "the index to place height=" << p.h << " , is at pos=" << curr << endl;
result[curr] = p.h;
countSet.erase(countSet.begin() + p.ct);
}
}
return result;
}
I'm using LinkedList for the this. Sort the tallCount[] in ascending order and accordingly re-position the items in heights[]. This is capable of handling the duplicate elements also.
public class FindHeightOrder {
public int[] findOrder(final int[] heights, final int[] tallCount) {
if (heights == null || heights.length == 0 || tallCount == null
|| tallCount.length == 0 || tallCount.length != heights.length) {
return null;
}
LinkedList list = new LinkedList();
list.insertAtStart(heights[0]);
for (int i = 1; i < heights.length; i++) {
if (tallCount[i] == 0) {
Link temp = list.getHead();
while (temp != null && temp.getData() <= heights[i]) {
temp = temp.getLink();
}
if (temp != null) {
if (temp.getData() <= heights[i]) {
list.insertAfterElement(temp.getData(), heights[i]);
} else {
list.insertAtStart(heights[i]);
}
} else {
list.insertAtEnd(heights[i]);
}
} else {
Link temp = list.getHead();
int pos = tallCount[i];
while (temp != null
&& (temp.getData() <= heights[i] || pos-- > 0)) {
temp = temp.getLink();
}
if (temp != null) {
if (temp.getData() <= heights[i]) {
list.insertAfterElement(temp.getData(), heights[i]);
} else {
list.insertBeforeElement(temp.getData(), heights[i]);
}
} else {
list.insertAtEnd(heights[i]);
}
}
}
Link fin = list.getHead();
int i = 0;
while (fin != null) {
heights[i++] = fin.getData();
fin = fin.getLink();
}
return heights;
}
public class Link {
private int data;
private Link link;
public Link(int data) {
this.data = data;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public Link getLink() {
return link;
}
public void setLink(Link link) {
this.link = link;
}
#Override
public String toString() {
return this.data + " -> "
+ (this.link != null ? this.link : "null");
}
}
public class LinkedList {
private Link head;
public Link getHead() {
return head;
}
public void insertAtStart(int data) {
if (head == null) {
head = new Link(data);
head.setLink(null);
} else {
Link link = new Link(data);
link.setLink(head);
head = link;
}
}
public void insertAtEnd(int data) {
if (head != null) {
Link temp = head;
while (temp != null && temp.getLink() != null) {
temp = temp.getLink();
}
temp.setLink(new Link(data));
} else {
head = new Link(data);
}
}
public void insertAfterElement(int after, int data) {
if (head != null) {
Link temp = head;
while (temp != null) {
if (temp.getData() == after) {
Link link = new Link(data);
link.setLink(temp.getLink());
temp.setLink(link);
break;
} else {
temp = temp.getLink();
}
}
}
}
public void insertBeforeElement(int before, int data) {
if (head != null) {
Link current = head;
Link previous = null;
Link ins = new Link(data);
while (current != null) {
if (current.getData() == before) {
ins.setLink(current);
break;
} else {
previous = current;
current = current.getLink();
if (current != null && current.getData() == before) {
previous.setLink(ins);
ins.setLink(current);
break;
}
}
}
}
}
#Override
public String toString() {
return "LinkedList [head=" + this.head + "]";
}
}
}
As people already corrected for original input:
Heights : A[] = { 5 3 2 6 1 4 }
InFronts: B[] = { 0 1 2 0 3 2 }
Output should look like: X[] = { 5 3 1 6 2 4 }
Here is the O(N*logN) way to approach solution (with assumption that there are no ties).
Iterate over array B and build chain of inequalities (by placing items into a right spot on each iteration, here we can use hashtable for O(1) lookups):
b0 > b1
b0 > b1 > b2
b3 > b0 > b1 > b2
b3 > b0 > b1 > b4 > b2
b3 > b0 > b5 > b1 > b4 > b2
Sort array A and reverse it
Initialize output array X, iterate over chain from #1 and fill array X by placing items from A into a position defined in a chain
Steps #1 and #3 are O(N), step #2 is the most expensive O(N*logN).
And obviously reversing sorted array A (in step #2) is not required.
This is the implementation for the idea provided by user1990169. Complexity being O(N^2).
public class Solution {
class Person implements Comparator<Person>{
int height;
int infront;
public Person(){
}
public Person(int height, int infront){
this.height = height;
this.infront = infront;
}
public int compare(Person p1, Person p2){
return p1.height - p2.height;
}
}
public ArrayList<Integer> order(ArrayList<Integer> heights, ArrayList<Integer> infronts) {
int n = heights.size();
Person[] people = new Person[n];
for(int i = 0; i < n; i++){
people[i] = new Person(heights.get(i), infronts.get(i));
}
Arrays.sort(people, new Person());
Person[] rst = new Person[n];
for(Person p : people){
int count = 0;
for(int i = 0; i < n ; i++){
if(count == p.infront){
while(rst[i] != null && i < n - 1){
i++;
}
rst[i] = p;
break;
}
if(rst[i] == null) count++;
}
}
ArrayList<Integer> heightrst = new ArrayList<Integer>();
for(int i = 0; i < n; i++){
heightrst.add(rst[i].height);
}
return heightrst;
}
}
Was solving this problem today, here is what I came up with:
The idea is to sort the heights array in descending order. Once, we have this sorted array - pick up an element from this element and place it in the resultant array at the corresponding index (I am using an ArrayList for the same, it would be nice to use LinkedList) :
public class Solution {
public ArrayList<Integer> order(ArrayList<Integer> heights, ArrayList<Integer> infronts) {
Person[] persons = new Person[heights.size()];
ArrayList<Integer> res = new ArrayList<>();
for (int i = 0; i < persons.length; i++) {
persons[i] = new Person(heights.get(i), infronts.get(i));
}
Arrays.sort(persons, (p1, p2) -> {
return Integer.compare(p2.height, p1.height);
});
for (int i = 0; i < persons.length; i++) {
//System.out.println("adding "+persons[i].height+" "+persons[i].count);
res.add(persons[i].count, persons[i].height);
}
return res;
}
private static class Person {
public final int height;
public final int count;
public Person(int h, int c) {
height = h;
count = c;
}
}
}
I found this kind of problem on SPOJ. I created a binary tree with little variation. When a new height is inserted, if the front is smaller than the root's front then it goes to the left otherwise right.
Here is the C++ implementation:
#include<bits/stdc++.h>
using namespace std;
struct TreeNode1
{
int val;
int _front;
TreeNode1* left;
TreeNode1*right;
};
TreeNode1* Add(int x, int v)
{
TreeNode1* p= (TreeNode1*) malloc(sizeof(TreeNode1));
p->left=NULL;
p->right=NULL;
p->val=x;
p->_front=v;
return p;
}
TreeNode1* _insert(TreeNode1* root, int x, int _front)
{
if(root==NULL) return Add(x,_front);
if(root->_front >=_front)
{
root->left=_insert(root->left,x,_front);
root->_front+=1;
}
else
{
root->right=_insert(root->right,x,_front-root->_front);
}
return root;
}
bool comp(pair<int,int> a, pair<int,int> b)
{
return a.first>b.first;
}
void in_order(TreeNode1 * root, vector<int>&v)
{
if(root==NULL) return ;
in_order(root->left,v);
v.push_back(root->val);
in_order(root->right,v);
}
vector<int>soln(vector<int>h, vector<int>in )
{
vector<pair<int , int> >vc;
for(int i=0;i<h.size();i++) vc.push_back( make_pair( h[i],in[i] ) );
sort(vc.begin(),vc.end(),comp);
TreeNode1* root=NULL;
for(int i=0;i<vc.size();i++)
root=_insert(root,vc[i].first,vc[i].second);
vector<int>v;
in_order(root,v);
return v;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
vector<int>h;
vector<int>in;
for(int i=0;i<n;i++) {int x;
cin>>x;
h.push_back(x);}
for(int i=0;i<n;i++) {int x; cin>>x;
in.push_back(x);}
vector<int>v=soln(h,in);
for(int i=0;i<n-1;i++) cout<<v[i]<<" ";
cout<<v[n-1]<<endl;
h.clear();
in.clear();
}
}
Here is a Python solution that uses only elementary list functions and takes care of ties.
def solution(heights, infronts):
person = list(zip(heights, infronts))
person.sort(key=lambda x: (x[0] == 0, x[1], -x[0]))
output = []
for p in person:
extended_output = output + [p]
extended_output.sort(key=lambda x: (x[0], -x[1]))
output_position = [p for p in extended_output].index(p) + p[1]
output.insert(output_position, p)
for c, p in enumerate(output):
taller_infronts = [infront for infront in output[0:c] if infront[0] >= p[0]]
assert len(taller_infronts) == p[1]
return output
Simple O(n^2) solution for this in Java:
Algorith:
If the position of the shortest person is i, i-1 taller people will be in front of him.
We fix the position of shortest person and then move to second shortest person.
Sort people by heights. Then iterate from shortest to tallest. In each step you need an efficient way to put the next person to the correct position.
We can optimise this solution even more by using segment tree. See this link.
class Person implements Comparable<Person>{
int height;
int pos;
Person(int height, int pos) {
this.height = height;
this.pos = pos;
}
#Override
public int compareTo(Person person) {
return this.height - person.height;
}
}
public class Solution {
public int[] order(int[] heights, int[] positions) {
int n = heights.length;
int[] ans = new int[n];
PriorityQueue<Person> pq = new PriorityQueue<Person>();
for( int i=0; i<n; i++) {
pq.offer(new Person(heights[i], positions[i]) );
}
for(int i=0; i<n; i++) {
Person person = pq.poll();
int vacantTillNow = 0;
int index = 0;
while(index < n) {
if( ans[index] == 0) vacantTillNow++;
if( vacantTillNow > person.pos) break;
index++;
}
ans[index] = person.height;
}
return ans;
}
}
Segment tree can be used to solve this in O(nlog n) if there are no ties in heights.
Please look for approach 3 in this link for a clear explanation of this method.
https://www.codingninjas.com/codestudio/problem-details/order-of-people-heights_1170764
Below is my code for the same approach in python
def findEmptySlot(tree, root, left, right, K, result):
tree[root]-=1
if left==right:
return left
if tree[2*root+1] >= K:
return findEmptySlot(tree, 2*root+1, left, (left+right)//2, K, result)
else:
return findEmptySlot(tree, 2*root+2, (left+right)//2+1, right, K-tree[2*root+1], result)
def buildsegtree(tree, pos, start, end):
if start==end:
tree[pos]=1
return tree[pos]
mid=(start+end)//2
left = buildsegtree(tree, 2*pos+1,start, mid)
right = buildsegtree(tree,2*pos+2,mid+1, end)
tree[pos]=left+right
return tree[pos]
class Solution:
# #param A : list of integers
# #param B : list of integers
# #return a list of integers
def order(self, A, B):
n=len(A)
people=[(A[i],B[i]) for i in range(len(A))]
people.sort(key=lambda x: (x[0], x[1]))
result=[0]*n
tree=[0]*(4*n)
buildsegtree(tree,0, 0, n-1)
for i in range(n):
idx=findEmptySlot(tree, 0, 0, n-1, people[i][1]+1, result)
result[idx]=people[i][0]
return result

Interview - Oracle

In a game the only scores which can be made are 2,3,4,5,6,7,8 and they can be made any number of times
What are the total number of combinations in which the team can play and the score of 50 can be achieved by the team.
example 8,8,8,8,8,8,2 is valid 8,8,8,8,8,4,4,2 is also valid. etc...
The problem can be solved with dynamic programming, with 2 parameters:
i - the index up to which we have considered
s - the total score.
f(i, s) will contain the total number of ways to achieve score s.
Let score[] be the list of unique positive scores that can be made.
The formulation for the DP solution:
f(0, s) = 1, for all s divisible to score[0]
f(0, s) = 0, otherwise
f(i + 1, s) = Sum [for k = 0 .. floor(s/score[i + 1])] f(i, s - score[i + 1] * k)
This looks like a coin change problem. I wrote some Python code for it a while back.
Edited Solution:
from collections import defaultdict
my_dicto = defaultdict(dict)
def row_analysis(v, my_dicto, coins):
temp = 0
for coin in coins:
if v >= coin:
if v - coin == 0: # changed from if v - coin in (0, 1):
temp += 1
my_dicto[coin][v] = temp
else:
temp += my_dicto[coin][v - coin]
my_dicto[coin][v] = temp
else:
my_dicto[coin][v] = temp
return my_dicto
def get_combs(coins, value):
'''
Returns answer for coin change type problems.
Coins are assumed to be sorted.
Example:
>>> get_combs([1,2,3,5,10,15,20], 50)
2955
'''
dicto = defaultdict(dict)
for v in xrange(value + 1):
dicto = row_analysis(v, dicto, coins)
return dicto[coins[-1]][value]
In your case:
>>> get_combs([2,3,4,5,6,7,8], 50)
3095
It is like visit a 7-branches decision tree.
The code is:
class WinScore{
static final int totalScore=50;
static final int[] list={2,3,4,5,6,7,8};
public static int methodNum=0;
static void visitTree( int achieved , int index){
if (achieved >= totalScore ){
return;
}
for ( int i=index; i< list.length; i++ ){
if ( achieved + list[i] == totalScore ) {
methodNum++;
}else if ( achieved + list[i] < totalScore ){
visitTree( achieved + list[i], i );
}
}
}
public static void main( String[] args ){
visitTree(0, 0);
System.out.println("number of methods are:" + methodNum );
}
}
output:
number of methods are:3095
Just stumbled on this question - here's a c# variation which allows you to explore the different combinations:
static class SlotIterator
{
public static IEnumerable<string> Discover(this int[] set, int maxScore)
{
var st = new Stack<Slot>();
var combinations = 0;
set = set.OrderBy(c => c).ToArray();
st.Push(new Slot(0, 0, set.Length));
while (st.Count > 0)
{
var m = st.Pop();
for (var i = m.Index; i < set.Length; i++)
{
if (m.Counter + set[i] < maxScore)
{
st.Push(m.Clone(m.Counter + set[i], i));
}
else if (m.Counter + set[i] == maxScore)
{
m.SetSlot(i);
yield return m.Slots.PrintSlots(set, ++combinations, maxScore);
}
}
}
}
public static string PrintSlots(this int[] slots, int[] set, int numVariation, int maxScore)
{
var sb = new StringBuilder();
var accumulate = 0;
for (var j = 0; j < slots.Length; j++)
{
if (slots[j] <= 0)
{
continue;
}
var plus = "+";
for (var k = 0; k < slots[j]; k++)
{
accumulate += set[j];
if (accumulate == maxScore) plus = "";
sb.AppendFormat("{0}{1}", set[j], plus);
}
}
sb.AppendFormat("={0} - Variation nr. {1}", accumulate, numVariation);
return sb.ToString();
}
}
public class Slot
{
public Slot(int counter, int index, int countSlots)
{
this.Slots = new int[countSlots];
this.Counter = counter;
this.Index = index;
}
public void SetSlot(int index)
{
this.Slots[index]++;
}
public Slot Clone(int newval, int index)
{
var s = new Slot(newval, index, this.Slots.Length);
this.Slots.CopyTo(s.Slots, 0);
s.SetSlot(index);
return s;
}
public int[] Slots { get; private set; }
public int Counter { get; set; }
public int Index { get; set; }
}
Example:
static void Main(string[] args)
{
using (var sw = new StreamWriter(#"c:\test\comb50.txt"))
{
foreach (var s in new[] { 2, 3, 4, 5, 6, 7, 8 }.Discover(50))
{
sw.WriteLine(s);
}
}
}
Yields 3095 combinations.

An even and sorted distribution problem

I have a given number of boxes in a specific order and a number of weights in a specific order. The weights may have different weights (ie one may weigh 1kg, another 2kg etc).
I want to put the weights in the boxes in a way so that they are as evenly distributed as possible weight wise. I must take the weights in the order that they are given and I must fill the boxes in the order that they are given. That is if I put a weight in box n+1 I cannot put a weight in box n, and I cannot put weight m+1 in a box until I've first put weight m in a box.
I need to find an algorithm that solves this problem for any number of boxes and any set of weights.
A few tests in C# with xUnit (Distribute is the method that should solve the problem):
[Fact]
public void ReturnsCorrectNumberOfBoxes()
{
int[] populatedColumns = Distribute(new int[0], 4);
Assert.Equal<int>(4, populatedColumns.Length);
}
[Fact]
public void Test1()
{
int[] weights = new int[] { 1, 1, 1, 1 };
int[] boxes = Distribute(weights, 4);
Assert.Equal<int>(weights[0], boxes[0]);
Assert.Equal<int>(weights[1], boxes[1]);
Assert.Equal<int>(weights[2], boxes[2]);
Assert.Equal<int>(weights[3], boxes[3]);
}
[Fact]
public void Test2()
{
int[] weights = new int[] { 1, 1, 17, 1, 1 };
int[] boxes = Distribute(weights, 4);
Assert.Equal<int>(2, boxes[0]);
Assert.Equal<int>(17, boxes[1]);
Assert.Equal<int>(1, boxes[2]);
Assert.Equal<int>(1, boxes[3]);
}
[Fact]
public void Test3()
{
int[] weights = new int[] { 5, 4, 6, 1, 5 };
int[] boxes = Distribute(weights, 4);
Assert.Equal<int>(5, boxes[0]);
Assert.Equal<int>(4, boxes[1]);
Assert.Equal<int>(6, boxes[2]);
Assert.Equal<int>(6, boxes[3]);
}
Any help is greatly appreciated!
See the solution below.
Cheers,
Maras
public static int[] Distribute(int[] weights, int boxesNo)
{
if (weights.Length == 0)
{
return new int[boxesNo];
}
double average = weights.Average();
int[] distribution = new int[weights.Length];
for (int i = 0; i < distribution.Length; i++)
{
distribution[i] = 0;
}
double avDeviation = double.MaxValue;
List<int> bestResult = new List<int>(boxesNo);
while (true)
{
List<int> result = new List<int>(boxesNo);
for (int i = 0; i < boxesNo; i++)
{
result.Add(0);
}
for (int i = 0; i < weights.Length; i++)
{
result[distribution[i]] += weights[i];
}
double tmpAvDeviation = 0;
for (int i = 0; i < boxesNo; i++)
{
tmpAvDeviation += Math.Pow(Math.Abs(average - result[i]), 2);
}
if (tmpAvDeviation < avDeviation)
{
bestResult = result;
avDeviation = tmpAvDeviation;
}
if (distribution[weights.Length - 1] < boxesNo - 1)
{
distribution[weights.Length - 1]++;
}
else
{
int index = weights.Length - 1;
while (distribution[index] == boxesNo - 1)
{
index--;
if (index == -1)
{
return bestResult.ToArray();
}
}
distribution[index]++;
for (int i = index; i < weights.Length; i++)
{
distribution[i] = distribution[index];
}
}
}
}
Second try: i think the A* (pronounced "a star") algorithm would work well here, even if it would consume a lot of memory. you are guranteed to get an optimal answer, if one exists.
Each "node" you are searching is a possible combination of weights in boxes. The first node should be any weight you pick at random, put into a box. I would recommend picking new weights randomly as well.
Unforetunately, A* is complex enough that I don't have time to explain it here. It is easy enough to understand by reading on your own, but mapping it to this problem as I described above will be more difficult. Please post back questions on that if you choose this route.

Resources