Given the following Input
10 4 3 5 5 7
Where
10 = Total Score
4 = 4 players
3 = Score by player 1
5 = Score by player 2
Here's the super naive solution that simply generates a power set on your input array and then iterates over each set to see if the sum satisfies the given total. I hacked it together with code already available on StackOverflow.
O(2n) in time and space. Gross.
You can use the idea of a Set
to store all indices into your arrays, then generate all permutations of those indices, and then use each set of indices to then go back into your array and get the values.
10
[3, 5, 5, 7]
import java.util.*;
import java.lang.*;
import java.io.*;
class SubsetSum
{
public static <T> Set<Set<T>> powerSet(Set<T> originalSet)
{
Set<Set<T>> sets = new HashSet<Set<T>>();
if (originalSet.isEmpty())
{
sets.add(new HashSet<T>());
return sets;
}
List<T> list = new ArrayList<T>(originalSet);
T head = list.get(0);
Set<T> rest = new HashSet<T>(list.subList(1, list.size()));
for (Set<T> set : powerSet(rest))
{
Set<T> newSet = new HashSet<T>();
newSet.add(head);
newSet.addAll(set);
sets.add(newSet);
sets.add(set);
}
return sets;
}
public static void main(String[] args)
{
Set<Integer> mySet = new HashSet<Integer>();
int[] arr={3, 5, 5, 7};
int target = 10;
int numVals = 4;
for(int i=0;i<numVals;++i)
{
mySet.add(i);
}
System.out.println("Solutions: ");
for (Set<Integer> s : powerSet(mySet))
{
int sum = 0;
for (Integer e : s)
{
sum += arr[e];
}
if (sum == target)
{
String soln = "[ ";
for (Integer e : s)
{
soln += arr[e];
soln += " ";
}
soln += "]";
System.out.println(soln);
}
}
}
}
Solutions:
[ 5 5 ]
[ 3 7 ]
Live Demo
Once you understand this, perhaps you are ready to begin branch and bound or approximation approaches.
I might try a few things.
First, you're passing in an array with 4 values but later you say there are only three players. I think that's causing some of your difficulties.
For the fastest way I can think of to program this, I might try this.
public static void getSolution(int[] array, int desiredTotal) {
for (int firstIndex = 0; firstIndex < array.size - 1; ++firstIndex) {
getSolutionWith(array, desiredTotal, firstIndex);
}
}
public static void getSolutionWith(int[] array, int desiredTotal, int firstIndex) {
int lookFor = desiredTotal - array[firstIndex];
for (int secondIndex = firstIndex + 1; secondIndex < array.size; ++secondIndex) {
if (array[secondIndex] == lookFor) {
System.out.printf("%d %d\n", firstIndex + 1, secondIndex + 1);
}
}
}
I haven't tested this code, so it might not be perfect. Basically you start at the 0 position (person 1) and you then look at everyone else to see if the first person's value + second person's value equals your total. If so, you print them.
public List<Integer> findSubsetWithSum(int[] score, int totalScore)
{
int players = score.length;
int[] cameFrom = new int[totalScore+1];
int[] pickedPlayer = new int[totalScore+1];
for (int s = 0; s <= totalScore; s++)
{
cameFrom[s] = -1;
pickedPlayer[s] = -1;
}
cameFrom[0] = 0;
for (int p = 0; p < players; p++)
{
for (int s = score[p]; s <= totalScore; s++)
{
if (cameFrom[s - score[p]] >= 0)
{
cameFrom[s] = s - score[p];
pickedPlayer[s] = p + 1;
}
}
}
List<Integer> picked = new ArrayList<Integer>();
for (int s = totalScore; s > 0 && cameFrom[s] >= 0; s = cameFrom[s])
{
picked.add(pickedPlayer[s]);
}
return picked;
}
Your problem is in this part of the code
if(arr[i] < x)
{
myArray[i][x] = myArray[i-1][x];
}
else
{
myArray[i][x] = myArray[i-1][x-arr[i]];
}
You have two situations
why? [3, 34, 4, 12, 5, 2]
Do not forget the part that DP has Optimal Substructure properties. For, finding sum is 9 we have to find all the sum before it, means 1 to 8. That is exactly you are doing by declaring a W+1
row. So when we calculate sum is 7, for first three values we have a result [3,34,4]
, we need to carry that result to next level.
So you need to modify previous code, to this
myArray[i][x] = myArray[i-1][x];//carrying previous result
if(x>=arr[i] )
{
if (myArray[i][x]==1){
myArray[i][x]=1;
}
else{
myArray[i][x] = myArray[i-1][x-arr[i]];
}
}
You also have array indexing issue. Your i
and x
both start from 1 and you never consider the index 0 which is actually your first player. you need to take arr[i-1]
value
so further update will look like this,
myArray[i][x] = myArray[i-1][x];//carrying previous result
if(x>=arr[i-1] )
{
if (myArray[i][x]==1){
myArray[i][x]=1;
}
else{
myArray[i][x] = myArray[i-1][x-arr[i-1]];
}
}
So final program will look like this
public boolean findSolution(int[] scores, int total) {
int W = total;
int players = scores.length;
boolean[][] myArray = new boolean[players + 1][total + 1];
for (int player = 0; player <= players; player++) {
myArray[player][0] = true;
}
for (int score = 1; score < total; score++) {
myArray[0][score] = false;
}
for (int player = 1; player <= players; player++) {
for (int score = 1; score <= total; score++) {
myArray[player][score] = myArray[player - 1][score];
if (score >= scores[player - 1]) {
myArray[player][score] = myArray[player - 1][score
- scores[player - 1]]
|| myArray[player][score];
}
}
}
return myArray[players][W];
}
Now for printing result, look into true values in the matrix. it shouldn't be difficult to find out which values are set and when it was set. print those index to get the result.