I need to design an algorithm to find the maximum value I can get from (stepping) along an int[] at predefined (step lengths).
Input is the number of times we can "use" each step length; and is given by n2, n5 and n10. n2 means that we move 2 spots in the array, n5 means 5 spots and n10 means 10 spots. We can only move forward (from left to right).
The int[] contains the values 1..5, the size of the array is (n2*2 + n5*5 + n10*10). The starting point is int[0].
Example: we start at int[0]. From here we can move to int[0+2] == 3, int[0+5] == 4 or int[0+10] == 1. Let's move to int[5] since it has the highest value. From int[5] we can move to int[5+2], int[5+5] or int[5+10] etc.
We should move along the array in step lengths of 2, 5 or 10 (and we can only use each step length n2-, n5- and n10-times) in such a manner that we step in the array to collect as high sum as possible.
The output is the maximum value possible.
public class Main {
private static int n2 = 5;
private static int n5 = 3;
private static int n10 = 2;
private static final int[] pokestops = new int[n2 * 2 + n5 * 5 + n10 * 10];
public static void main(String[] args) {
Random rand = new Random();
for (int i = 0; i < pokestops.length; i++) {
pokestops[i] = Math.abs(rand.nextInt() % 5) + 1;
}
System.out.println(Arrays.toString(pokestops));
//TODO: return the maximum value possible
}
}
This is an answer in pseudocode (I didn't run it, but it should work).
fill dp with -1.
dp(int id, int 2stepcount, int 5stepcount, int 10stepcount) {
if(id > array_length - 1) return 0;
if(dp[id][2stepcount][5stepcount][10stepcount] != -1) return dp[id][2stepcount][5stepcount][10stepcount];
else dp[id][2stepcount][5stepcount][10stepcount] = 0;
int 2step = 2stepcount < max2stepcount? dp(id + 2, 2stepcount + 1, 5stepcount, 10stepcount) : 0;
int 5step = 5stepcount < max5stepcount? dp(id + 5, 2stepcount, 5stepcount + 1, 10stepcount) : 0;
int 10step = 10stepcount < max10stepcount? dp(id + 10, 2stepcount, 5stepcount, 10stepcount + 1) : 0;
dp[id][2stepcount][5stepcount][10stepcount] += array[id] + max(2step, 5step, 10step);
return dp[id][2stepcount][5stepcount][10stepcount];
}
Call dp(0,0,0,0) and the answer is in dp[0][0][0][0].
If you wanna go backwards, then you do this:
fill dp with -1.
dp(int id, int 2stepcount, int 5stepcount, int 10stepcount) {
if(id > array_length - 1 || id < 0) return 0;
if(dp[id][2stepcount][5stepcount][10stepcount] != -1) return dp[id][2stepcount][5stepcount][10stepcount];
else dp[id][2stepcount][5stepcount][10stepcount] = 0;
int 2stepForward = 2stepcount < max2stepcount? dp(id + 2, 2stepcount + 1, 5stepcount, 10stepcount) : 0;
int 5stepForward = 5stepcount < max5stepcount? dp(id + 5, 2stepcount, 5stepcount + 1, 10stepcount) : 0;
int 10stepForward = 10stepcount < max10stepcount? dp(id + 10, 2stepcount, 5stepcount, 10stepcount + 1) : 0;
int 2stepBackward = 2stepcount < max2stepcount? dp(id - 2, 2stepcount + 1, 5stepcount, 10stepcount) : 0;
int 5stepBackward = 5stepcount < max5stepcount? dp(id - 5, 2stepcount, 5stepcount + 1, 10stepcount) : 0;
int 10stepBackward = 10stepcount < max10stepcount? dp(id - 10, 2stepcount, 5stepcount, 10stepcount + 1) : 0;
dp[id][2stepcount][5stepcount][10stepcount] += array[id] + max(2stepForward, 5stepForward, 10stepForward, 2stepBackward, 5backForward, 10backForward);
return dp[id][2stepcount][5stepcount][10stepcount];
}
But your paths don't get fulled explored, because we stop if the index is negative or greater than the array size - 1, you can add the wrap around functionality, I guess.
this is a solution but i am not sure how optimal it is !
i did some optimization on it but i think much more can be done
I posted it with the example written in question
import java.util.Arrays;
import java.util.Random;
public class FindMax {
private static int n2 = 5;
private static int n5 = 3;
private static int n10 = 2;
private static final int[] pokestops = new int[n2 * 2 + n5 * 5 + n10 * 10];
public static int findMaxValue(int n2, int n5, int n10, int pos, int[] pokestops) {
System.out.print("|");
if (n2 <= 0 || n5 <= 0 || n10 <= 0) {
return 0;
}
int first;
int second;
int third;
if (pokestops[pos] == 5 || ((first = findMaxValue(n2 - 1, n5, n10, pos + 2, pokestops)) == 5) || ((second = findMaxValue(n2, n5 - 1, n10, pos + 5, pokestops)) == 5) || ((third = findMaxValue(n2, n5, n10 - 1, pos + 10, pokestops)) == 5)) {
return 5;
}
return Math.max(Math.max(Math.max(first, second), third), pokestops[pos]);
}
public static void main(String[] args) {
Random rand = new Random();
for (int i = 0; i < pokestops.length; i++) {
pokestops[i] = Math.abs(rand.nextInt() % 5) + 1;
}
System.out.println(Arrays.toString(pokestops));
//TODO: return the maximum value possible
int max = findMaxValue(n2, n5, n10, 0, pokestops);
System.out.println("");
System.out.println("Max is :" + max);
}
}
You need to calculate following dynamic programming dp[c2][c5][c10][id] - where c2 is number of times you've stepped by 2, c5 - by 5, c10 - by 10 and id - where is your current position. I will write example for c2 and c5 only, it can be easily extended.
int[][][][] dp = new int[n2 + 1][n5 + 1][pokestops.length + 1];
for (int[][][] dp2 : dp) for (int[][] dp3 : dp2) Arrays.fill(dp3, Integer.MAX_VALUE);
dp[0][0][0] = pokestops[0];
for (int c2 = 0; c2 <= n2; c2++) {
for (int c5 = 0; c5 <= n5; c5++) {
for (int i = 0; i < pokestops.length; i++) {
if (c2 < n2 && dp[c2 + 1][c5][i + 2] < dp[c2][c5][i] + pokestops[i + 2]) {
dp[c2 + 1][c5][i + 2] = dp[c2][c5][i] + pokestops[i + 2];
}
if (c5 < n5 && dp[c2][c5 + 1][i + 5] < dp[c2][c5][i] + pokestops[i + 5]) {
dp[c2][c5 + 1][i + 5] = dp[c2][c5][i] + pokestops[i + 5];
}
}
}
}
I know the target language is java, but I like pyhton and conversion will not be complicated.
You can define a 4-dimensional array dp where dp[i][a][b][c] is the maximum value that you can
get starting in position i when you already has a steps of length 2, b of length 5 and c of length
10. I use memoization to get a cleaner code.
import random
values = []
memo = {}
def dp(pos, n2, n5, n10):
state = (pos, n2, n5, n10)
if state in memo:
return memo[state]
res = values[pos]
if pos + 2 < len(values) and n2 > 0:
res = max(res, values[pos] + dp(pos + 2, n2 - 1, n5, n10))
if pos + 5 < len(values) and n5 > 0:
res = max(res, values[pos] + dp(pos + 5, n2, n5 - 1, n10))
if pos + 10 < len(values) and n10 > 0:
res = max(res, values[pos] + dp(pos + 10, n2, n5, n10 - 1))
memo[state] = res
return res
n2, n5, n10 = 5, 3, 2
values = [random.randint(1, 5) for _ in range(n2*2 + n5*5 + n10*10)]
print dp(0, n2, n5, n10)
Suspiciously like homework. Not tested:
import java.util.Arrays;
import java.util.Random;
public class Main {
private static Step[] steps = new Step[]{
new Step(2, 5),
new Step(5, 3),
new Step(10, 2)
};
private static final int[] pokestops = new int[calcLength(steps)];
private static int calcLength(Step[] steps) {
int total = 0;
for (Step step : steps) {
total += step.maxCount * step.size;
}
return total;
}
public static void main(String[] args) {
Random rand = new Random();
for (int i = 0; i < pokestops.length; i++) {
pokestops[i] = Math.abs(rand.nextInt() % 5) + 1;
}
System.out.println(Arrays.toString(pokestops));
int[] initialCounts = new int[steps.length];
for (int i = 0; i < steps.length; i++) {
initialCounts[i] = steps[i].maxCount;
}
Counts counts = new Counts(initialCounts);
Tree base = new Tree(0, null, counts);
System.out.println(Tree.max.currentTotal);
}
static class Tree {
final int pos;
final Tree parent;
private final int currentTotal;
static Tree max = null;
Tree[] children = new Tree[steps.length*2];
public Tree(int pos, Tree parent, Counts counts) {
this.pos = pos;
this.parent = parent;
if (pos < 0 || pos >= pokestops.length || counts.exceeded()) {
currentTotal = -1;
} else {
int tmp = parent == null ? 0 : parent.currentTotal;
this.currentTotal = tmp + pokestops[pos];
if (max == null || max.currentTotal < currentTotal) max = this;
for (int i = 0; i < steps.length; i++) {
children[i] = new Tree(pos + steps[i].size, this, counts.decrement(i));
// uncomment to allow forward-back traversal:
//children[2*i] = new Tree(pos - steps[i].size, this, counts.decrement(i));
}
}
}
}
static class Counts {
int[] counts;
public Counts(int[] counts) {
int[] tmp = new int[counts.length];
System.arraycopy(counts, 0, tmp, 0, counts.length);
this.counts = tmp;
}
public Counts decrement(int i) {
int[] tmp = new int[counts.length];
System.arraycopy(counts, 0, tmp, 0, counts.length);
tmp[i] -= 1;
return new Counts(tmp);
}
public boolean exceeded() {
for (int count : counts) {
if (count < 0) return true;
}
return false;
}
}
static class Step {
int size;
int maxCount;
public Step(int size, int maxCount) {
this.size = size;
this.maxCount = maxCount;
}
}
}
There's a line you can uncomment to allow forward and back movement (I'm sure someone said in the comments that was allowed, but now I see in your post it says forward only...)
Related
THE PROBLEM: Write a program that reads N points in a plane and outputs any group of four or more colinear points
What I did: I calculated the slope of every 2 points and then put them in a hashmap to see which one is the max.
What I need to do: I need to output the points that are colinear from the initial 2D array. I found the number of points that are colinear with the hashmap.
import java.util.*;
public class app {
public static int maxPoints(int[][] points) {
int ans = 1;
int n = points.length;
for (int i = 0; i < n; i++) {
HashMap<String, Integer> map = new HashMap<>();
int OLP = 0;
int max = 0;
for (int j = i + 1; j < n; j++) {
if (points[i][0] == points[j][0] && points[i][1] == points[j][1]) {
OLP++;
continue;
}
int dy = points[j][1] - points[i][1];
int dx = points[j][0] - points[i][0];
int g = gcd(Math.abs(dy), Math.abs(dx));
int num = dy / g;
int deno = dx / g;
if (num == 0)
deno = 1;
if (deno == 0)
num = 1;
if ((num < 0 && deno < 0) || deno < 0) {
num *= -1;
deno *= -1;
}
map.put(createString(deno, num), map.getOrDefault(createString(deno, num), 0) + 1);
max = Math.max(max, map.get(createString(deno, num)));
}
ans = Math.max(ans, 1 + OLP + max);
}
return ans;
}
public static int gcd(int a, int b) {
if (a == 0)
return b;
if (b == 0)
return a;
int max = Math.max(a, b);
int min = Math.min(a, b);
return gcd(max % min, min);
}
public static String createString(int a, int b) {
return Integer.toString(a) + " " + Integer.toString(b);
}
public static void main(String[] args) {
int[][] points = { { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 4 } };
System.out.println("max Points are: " + maxPoints(points));
}}
I want my program to randomly generate 1 and 0 but it should look like it's in a queue. The 1 is represented by a person and 0 means that there's no person. It should generate random 1 and 0 like this 0 0 0 0 1 1 1 1 1 1, if the line is in the left and vice versa if it's in the right, not like this 1 1 1 0 0 1 0 0 1 1.
public void randPeople(){
int person1 = rand.nextInt((1 - 0) + 1) + 0;
int person2 = rand.nextInt((1 - 0) + 1) + 0;
int person3 = rand.nextInt((1 - 0) + 1) + 0;
int person4 = rand.nextInt((1 - 0) + 1) + 0;
int person5 = rand.nextInt((1 - 0) + 1) + 0;
int person6 = rand.nextInt((1 - 0) + 1) + 0;
int person7 = rand.nextInt((1 - 0) + 1) + 0;
int person8 = rand.nextInt((1 - 0) + 1) + 0;
int person9 = rand.nextInt((1 - 0) + 1) + 0;
int person10 = rand.nextInt((1 - 0) + 1) + 0;
EntryFloor1.setText(Integer.toString(person1) + " " + Integer.toString(person2) + " " +
Integer.toString(person3) + " " + Integer.toString(person4) + " " +
Integer.toString(person5) + " " + Integer.toString(person6) + " " +
Integer.toString(person7) + " " + Integer.toString(person8) + " " +
Integer.toString(person9) + " " + Integer.toString(person10));
}
Implemented a simple random function to generate 0s and 1s
int[] queue = new int[10];
Random r = new Random();
int rand = r.nextInt(queue.length);
int r1 = 1 - rand % 2;
int r2 = rand % 2;
for (int i = 0; i < queue.length; i++) {
if (i <= rand) {
queue[i] = r1;
} else {
queue[i] = r2;
}
}
System.out.println("Queue " + Arrays.toString(queue));
output
Queue [1, 1, 1, 0, 0, 0, 0, 0, 0, 0]
With Java8 generator
final int size = 10;
final Random random = new Random();
boolean order = random.nextBoolean();
Object[] arr = IntStream.generate(() -> random.nextInt(size) % 2).limit(size).boxed().sorted((i1, i2) -> order ? i1 - i2 : i2 - i1).toArray();
System.out.println("Arrays " + Arrays.toString(arr));
output
Arrays [1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
I believe this will do what you want. Just as a side note - if you only care about whether or not a person exists I would suggest using boolean values to represent the people. Since you specifically said 1 and 0 I wrote the code below to produce that output. You'll notice that I do use the nextBoolean() method of the Random class as it is simpler and more readable for what I'm doing.
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class RandPeopleMain {
public static void main(String[] args) {
System.out.println(lineUpLeft(10));
System.out.println(lineUpRight(10));
}
public static List<Integer> lineUpLeft(int numPeople){
Random rand = new Random();
List<Integer> list = new ArrayList<Integer>();
for(int i=0; i < numPeople; i++){
boolean person = rand.nextBoolean();
if(person){
list.add(1);
}
}
for(int i=list.size(); i < numPeople; i++){
list.add(0);
}
return list;
}
public static List<Integer> lineUpRight(int numPeople){
Random rand = new Random();
List<Integer> list = new ArrayList<Integer>();
for(int i=0; i < numPeople; i++){
boolean person = rand.nextBoolean();
if(person){
list.add(1);
}
}
for(int i=list.size(); i < numPeople; i++){
list.add(0, 0);
}
return list;
}
}
Example output:
[1, 1, 1, 1, 1, 1, 0, 0, 0, 0]
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
I provided the above methods to illustrate how similar aligning the people left or right can be. Here is a more generic version that uses a single method with an additional parameter to specify the alignment.
public static List<Integer> lineUpPeople(int numPeople, boolean alignLeft){
Random rand = new Random();
List<Integer> list = new ArrayList<Integer>();
for(int i=0; i < numPeople; i++){
boolean person = rand.nextBoolean();
if(person){
list.add(1);
}
}
for(int i=list.size(); i < numPeople; i++){
if(alignLeft)
list.add(0);
else
list.add(0,0);
}
return list;
}
Lastly, here is another way to approach the problem. Rather than generating a random value for each potential person simply generate the number of people and then fill in the list/queue appropriately.
public static List<Integer> lineUp(int maxPeople, boolean alignLeft){
Random rand = new Random();
int numPeople = rand.nextInt(maxPeople+1);
List<Integer> list = new ArrayList<Integer>();
for(int i=0; i < maxPeople; i++){
if(i < numPeople)
list.add(1);
else if(alignLeft)
list.add(0);
else
list.add(0,0);
}
return list;
}
Try this:
Random r = new Random();
boolean b = r.nextBoolean(); // left or right
int l = r.nextInt(11); // breakpoint to change from 0 to 1 or other way
System.out.println(b + " " + l);
int person[] = new int[10];
for (int i = 0; i < 10; i++) {
if (b) {
if (i < l)
person[i] = 1;
else
person[i] = 0;
} else {
if (i < l)
person[i] = 0;
else
person[i] = 1;
}
}
System.out.println(Arrays.toString(person));
I have some code that will brute force solve the following problem:
Given a set of x coins and a target sum to reach, what is the fewest number of coins required to reach that target?
The code so far:
import java.util.ArrayList;
import java.util.Arrays;
public class coinsSum {
public static int min = Integer.MAX_VALUE;
public static int[] combination;
public static final int TARGET = 59;
public static void main(String[] args) {
long start = System.nanoTime();
int[] validCoins = new int[] {1, 2, 5, 10, 20};
Arrays.sort(validCoins);
int len = validCoins.length;
ArrayList<Integer> maxList = new ArrayList<Integer>();
for(int c : validCoins) {
maxList.add(TARGET / c);
}
int[] max = new int[len];
for(int i = 0; i < len; i++) {
max[i] = maxList.get(i).intValue();
}
permutations(new int[len], max, validCoins, 0); // bread&butter
if(min != Integer.MAX_VALUE) {
System.out.println();
System.out.println("The combination " + Arrays.toString(combination) + " uses " + min + " coins to make the target of: " + TARGET);
} else {
System.out.println("The target was not reachable using these coins");
}
System.out.println("TOOK: " + (System.nanoTime() - start) / 1000000 + "ms");
}
public static void permutations(int[] workspace, int[] choices, int[] coins, int pos) {
if(pos == workspace.length) {
int sum = 0, coinCount = 0;
System.out.println("TRYING " + Arrays.toString(workspace));
for(int a = 0; a < coins.length; a++) {
sum += workspace[a] * coins[a];
coinCount += workspace[a];
}
if(sum == TARGET) {
// System.out.println(Arrays.toString(n)); //valid combinations
if(coinCount < min) {
min = coinCount;
combination = workspace;
System.out.println(Arrays.toString(combination)+" uses " + min + " coins");
}
}
return;
}
for(int i = 0; i <= choices[pos]; i++) {
workspace[pos] = i;
permutations(workspace, choices, coins, pos + 1);
}
}
}
This solution uses recursion, is there any way to do compute combinations in java using loops?
How else can all possible combinations be iterated through?
You can sort the array of coins. Then go from right to left, keep subtracting from the target value, untill the coin is bigger from the remaining value of target. Move left in the array of coins and repeat the process.
Example:
{1, 2, 5, 10, 20}
num = 59
Try coins from right to left:
59 - 20 = 39
So far coins used [20]
39 - 20 = 19
So far coins used [20,20]
19 - 20 = -1, Can't use 20!
19 - 10 = 9
So far coins used [20,20,10]
9 - 10 = -1, Can't use 10!
9 - 5 = 4
So far coins used [20,20,10,5]
4 - 5 = -1, Can't use 5!
4 - 2 = 2
So far coins used [20,20,10,5,2]
2 - 2 = 0
So far coins used [20,20,10,5,2,2]
Total coin used 6
Here is a solution in python that uses dynamic programming to find the minimum number of coins to reach a target value.
The algorithm works as follow
dp[i][target] = minimum number of coins required required to acheive target using first i coin
dp[i][target] = min(dp[i-1][target],dp[i-1][target-coin[i]]+1)
dp[i-1][target] denotes not using the ith coin
dp[i-1][target-coin[i]] denotes making use of ith coin
Since for each coin your are checking wheather to include it or not the algorithm is enumerating through all possible combination.
Here is an space optimized version of the above algorithm
maxvalue = 10 ** 9
def minchange(coins, target):
no_of_coins = len(coins)
dp = [maxvalue for i in range(target + 1) ]
dp[0] = 0
for i in range(no_of_coins):
for j in range(coins[i], target + 1):
dp[j] = min(dp[j], dp[j - coins[i]] + 1)
return dp[target]
I found a dynamic programming approach which is definitely not optimised, but isn't too bad for target numbers up to 10000 if anyone is interested
import java.util.*;
public class coinSumMinimalistic {
public static final int TARGET = 12003;
public static int[] validCoins = {1, 3, 5, 6, 7, 10, 12};
public static void main(String[] args) {
Arrays.sort(validCoins);
sack();
}
public static void sack() {
Map<Integer, Integer> coins = new TreeMap<Integer, Integer>();
coins.put(0, 0);
int a = 0;
for(int i = 1; i <= TARGET; i++) {
if(a < validCoins.length && i == validCoins[a]) {
coins.put(i, 1);
a++;
} else coins.put(i, -1);
}
for(int x = 2; x <= TARGET; x++) {
if(x % 5000 == 0) System.out.println("AT: " + x);
ArrayList<Integer> list = new ArrayList<Integer>();
for(int i = 0; i <= x / 2; i++) {
int j = x - i;
list.add(i);
list.add(j);
}
coins.put(x, min(list, coins));
}
System.out.println("It takes " + coins.get(TARGET) + " coins to reach the target of " + TARGET);
}
public static int min(ArrayList<Integer> combos, Map<Integer, Integer> coins) {
int min = Integer.MAX_VALUE;
int total = 0;
for(int i = 0; i < combos.size() - 1; i += 2) {
int x = coins.get(combos.get(i));
int y = coins.get(combos.get(i + 1));
if(x < 0 || y < 0) continue;
else {
total = x + y;
if(total > 0 && total < min) {
min = total;
}
}
}
int t = (min == Integer.MAX_VALUE || min < 0) ? -1:min;
return t;
}
public static void print(Map<Integer, Integer> map) {
for(Map.Entry<Integer, Integer> entry : map.entrySet()) {
System.out.println("[" + entry.getKey() + ", " + entry.getValue() + "]");
}
System.out.println();
}
}
I am working on a problem to try to find what is called a Snake Sequence in a typical XY graph (aka grid). A Snake sequence is defined as a sequence of numbers where each new number, which can only be located to the right or down of the current number, is either plus or minus one. For example, if you are in the center of the graph, you can either move right (if that number is + or - 1) or move down (if that number is + or - 1). The goal of the problem is to find the longest path (aka Snake Sequence) through the graph (keeping in mind you can only chart a path to a new cell whose value is +- 1 with the current cell).
So, for the following XY graph, the longest snake sequence is: 9, 8, 7, 6, 5, 6, 7
9, 6, 5, 2
8, 7, 6, 5
7, 3, 1, 6
1, 1, 1, 7
Below is my code, which does not seem to work.
Question: How would you solve this problem above? (I offer my code showing what I have thus far, but it does not work)
import java.util.ArrayList;
public class SnakeSequence {
private final int maxX = 3;
private final int maxY = 3;
private final int[][] board = new int[][]{
{1, 2, 3, 4},
{2, 1, -1, 5},
{3, 0, -1, 6},
{6, 2, 1, 7}
};
private ArrayList<Integer> findSequence(int xPos,
int yPos, ArrayList<Integer> currentPath) {
currentPath.add(board[yPos][xPos]);
ArrayList<Integer> pathRight = new ArrayList<Integer>(currentPath);
ArrayList<Integer> pathDown = new ArrayList<Integer>(currentPath);
if (xPos < maxX || yPos < maxY) {
if (yPos < maxY && (board[yPos + 1][xPos] + 1 == board[yPos][xPos] ||
board[yPos + 1][xPos] - 1 == board[yPos][xPos])) {
pathDown = findSequence(xPos, yPos + 1, currentPath);
}
if (xPos < maxX && (board[yPos][xPos + 1] + 1 == board[yPos][xPos] ||
board[yPos][xPos + 1] - 1 == board[yPos][xPos])) {
pathRight = findSequence(xPos + 1, yPos, currentPath);
}
if (pathDown.size() > pathRight.size()) {
return pathDown;
} else {
return pathRight;
}
}
return currentPath;
}
private void getSequence() {
ArrayList<Integer> currentPath = new ArrayList<Integer>();
ArrayList<Integer> result;
result = findSequence(0, 0, currentPath);
for (int i = 0; i < result.size(); i++) {
System.out.println(result.get(i));
}
}
public static void main(String[] args) {
SnakeSequence sequence = new SnakeSequence();
sequence.getSequence();
}
}
You can imagine your table as an oriented graph, then you problem is just to find the longest path.
Fortunatly for you, only moving down and right is allowed, so your graph is acyclic, so you can use algorithms like critical path method.
This is how your graph would look like:
However, you want to find longest path between any two cells. To do that, I would compute for each cell the longest path starting at that cell. It is simmilar to what you do, but you compute same thing more times. Consider this:
6 -> 5
| |
v v
7 -> 6
At both 5 and 7 you compute how long is the path from 6 at down right, and that is useless repeated computation. In worst case scenario, this could lead to exponential time consumption, while the problem can be resolved in linear time!
Moreover, there is no guarantee that the longest path will start at (0,0).
(one possible) Solution:
Compute longest path from each cell, starting from bottom-right to upper-left. At each cel.. remember how long the longest path from that cell is, and witch way from that cell the path leads. (I will measure path length by number of cells on it). So for example, for the only 8 in your grapth, we would remeber [length=8, direction=right].
Why so complicated? Because it is now extramly easy to compute longest path at a cell, if we know the longest path of the cells to the right and down. Example (I made up):
The correct data for 2 now would be [length=4, direction=down] because can't go from 2 to 4.
You can also keep globally longest path and it's start. After the computation is complete, just walk the longest path from that start through the direction and write the numbers, position or whatever you need.
Apologies for my Java (I am primarily a c# programmer) but here is one solution. I separated out the algorithm that discovers the snakes from the algorithm (implementing the interface ISnakeProcessor) that processes each one. That way you could enhance the code to, e.g., collect the snake with the largest sum of values, or collect all the longest snakes, in case there are more than one, by adding more ISnakeProcessor classes.
import java.util.*;
import java.lang.*;
class Rextester
{
public static void main(String args[])
{
SnakeSequence sequence = new SnakeSequence();
sequence.getSequence();
}
}
interface ISnakeProcessor
{
void process(List<Pair<Integer, Integer>> snake);
}
class SnakeSequence {
private final int[][] board;
public SnakeSequence()
{
this(new int[][]{
{1, 2, 3, 4},
{2, 1, -1, 5},
{3, 0, -1, 6},
{6, 2, 1, 7}
});
}
public SnakeSequence(int[][] board)
{
this.board = board;
}
public boolean isValid(int iRow, int iCol)
{
if (iRow < 0 || iRow >= board.length)
return false;
if (iCol < 0 || iCol >= board[iRow].length)
return false;
return true;
}
private boolean continuesInRow(int iRow, int iCol)
{
if (!isValid(iRow, iCol) || !isValid(iRow+1, iCol))
return false;
int myVal = board[iRow][iCol];
if (board[iRow+1][iCol] == myVal - 1 || board[iRow+1][iCol] == myVal + 1)
return true;
return false;
}
private boolean continuesInCol(int iRow, int iCol)
{
if (!isValid(iRow, iCol) || !isValid(iRow, iCol+1))
return false;
int myVal = board[iRow][iCol];
if (board[iRow][iCol+1] == myVal - 1 || board[iRow][iCol+1] == myVal + 1)
return true;
return false;
}
private boolean isHead(int iRow, int iCol)
{
if (!isValid(iRow, iCol))
return false;
if (isValid(iRow-1, iCol) && continuesInRow(iRow-1, iCol))
return false;
if (isValid(iRow, iCol-1) && continuesInRow(iRow, iCol-1))
return false;
return true;
}
private boolean isTail(int iRow, int iCol)
{
if (!isValid(iRow, iCol))
return false;
if (continuesInRow(iRow, iCol))
return false;
if (continuesInCol(iRow, iCol))
return false;
return true;
}
private void testHead()
{
System.out.println("Dumping list of heads");
for (int iRow = 0; iRow < board.length; iRow++)
{
for (int iCol = 0; iCol < board[iRow].length; iCol++)
{
boolean head = isHead(iRow, iCol);
boolean tail = isTail(iRow, iCol);
if (head && tail)
System.out.print(" B");
else if (head)
System.out.print(" H");
else if (tail)
System.out.print(" T");
else
System.out.print(" -");
}
System.out.println("");
}
}
private void walkSnake(ISnakeProcessor processor, int iRow, int iCol, ArrayList<Pair<Integer, Integer>> snake)
{
snake.add(new Pair<Integer, Integer>(iRow, iCol));
boolean isTail = true;
if (continuesInRow(iRow, iCol))
{
walkSnake(processor, iRow+1, iCol, snake);
isTail = false;
}
if (continuesInCol(iRow, iCol))
{
walkSnake(processor, iRow, iCol+1, snake);
isTail = false;
}
if (isTail)
{
processor.process(snake);
}
snake.remove(snake.size() - 1);
}
private void walkSnakes(ISnakeProcessor processor)
{
ArrayList<Pair<Integer, Integer>> snake = new ArrayList<Pair<Integer, Integer>>();
for (int iRow = 0; iRow < board.length; iRow++)
for (int iCol = 0; iCol < board[iRow].length; iCol++)
if (isHead(iRow, iCol))
walkSnake(processor, iRow, iCol, snake);
}
class LongestSnakeFinder implements ISnakeProcessor
{
private final SnakeSequence parent;
ArrayList<Pair<Integer, Integer>> longest = new ArrayList<Pair<Integer, Integer>>();
public LongestSnakeFinder(SnakeSequence parent)
{
this.parent = parent;
}
public void process(List<Pair<Integer, Integer>> snake)
{
if (snake.size() > longest.size())
{
longest.clear();
longest.addAll(snake);
}
}
public void dumpLongest()
{
System.out.format("The first encountered longest snake has length %d:\n", longest.size());
for (int i = 0; i < longest.size(); i++)
{
int iRow = longest.get(i).getFirst();
int iCol = longest.get(i).getSecond();
System.out.format(" (%d,%d): %d\n", iRow, iCol, parent.getValue(iRow, iCol));
}
}
}
public int getNRows() { return board.length; }
public int getNCols(int iRow) { return board[iRow].length; }
public int getValue(int iRow, int iCol) { return board[iRow][iCol]; }
public void getSequence() {
testHead();
LongestSnakeFinder finder = new LongestSnakeFinder(this);
walkSnakes(finder);
finder.dumpLongest();
}
}
class Pair<F, S> {
private F first; //first member of pair
private S second; //second member of pair
public Pair(F first, S second) {
this.first = first;
this.second = second;
}
public F getFirst() {
return first;
}
public S getSecond() {
return second;
}
}
Example run here: http://rextester.com/AKUFNL43897
Update - cleaned code a little. New sample run here: http://rextester.com/AVOAIY11573
And, the output:
Dumping list of heads
H - - -
- - B -
T - T -
B H T T
The first encountered longest snake has length 7:
(0,0): 1
(0,1): 2
(0,2): 3
(0,3): 4
(1,3): 5
(2,3): 6
(3,3): 7
Is this what you want?
Here is one simple way to correct your solution and avoid copying of path on every step
import java.util.ArrayList;
import java.util.Collections;
public class SnakeSequence {
private final int maxX = 3;
private final int maxY = 3;
private final int[][] board = new int[][]{
{1, 2, 3, 4},
{2, 1, -1, 5},
{3, 0, -1, 6},
{6, 2, 1, 7}
};
private ArrayList<Integer> findSequence(int xPos,
int yPos) {
ArrayList<Integer> pathRight = new ArrayList<Integer>();
ArrayList<Integer> pathDown = new ArrayList<Integer>();
if (yPos < maxY && (board[yPos + 1][xPos] + 1 == board[yPos][xPos] ||
board[yPos + 1][xPos] - 1 == board[yPos][xPos])) {
pathDown = findSequence(xPos, yPos + 1);
}
if (xPos < maxX && (board[yPos][xPos + 1] + 1 == board[yPos][xPos] ||
board[yPos][xPos + 1] - 1 == board[yPos][xPos])) {
pathRight = findSequence(xPos + 1, yPos);
}
ArrayList<Integer> ans;
if (pathDown.size() > pathRight.size()) {
ans = pathDown;
} else {
ans = pathRight;
}
ans.add(board[yPos][xPos]);
return ans;
}
private void getSequence() {
ArrayList<Integer> result;
result = findSequence(0, 0);
Collections.reverse(result);
for (int i = 0; i < result.size(); i++) {
System.out.println(result.get(i));
}
}
public static void main(String[] args) {
SnakeSequence sequence = new SnakeSequence();
sequence.getSequence();
}
}
But this way it can work much faster for big arrays due to no recalculating the longest path every time you visiting the same number during recursion. Actually, in this version each number is visited at most twice. It's achieved through saving best solution for every node. Separate storage of path and it length allows not to copy path when it's not needed.
import java.util.ArrayList;
import java.util.Collections;
public class SnakeSequence {
private final int maxX = 3;
private final int maxY = 3;
private final int[][] board = new int[][]{
{1, 2, 3, 4},
{2, 3, -1, 5},
{3, 2, -1, 6},
{6, 1, 2, 3}
};
int[][] pathLength;
ArrayList<ArrayList<ArrayList<Integer>>> paths;
private ArrayList<Integer> findSequence(int xPos,
int yPos) {
if(pathLength[yPos][xPos] >= 0)
{
ArrayList<Integer> ans = new ArrayList<Integer>();
int length = pathLength[yPos][xPos];
ArrayList<Integer> path = paths.get(yPos).get(xPos);
for(int i = 0; i < length; i++)
ans.add(path.get(i));
return ans;
}
ArrayList<Integer> pathRight = new ArrayList<Integer>();
ArrayList<Integer> pathDown = new ArrayList<Integer>();
if (yPos < maxY && (board[yPos + 1][xPos] + 1 == board[yPos][xPos] ||
board[yPos + 1][xPos] - 1 == board[yPos][xPos])) {
pathDown = findSequence(xPos, yPos + 1);
}
if (xPos < maxX && (board[yPos][xPos + 1] + 1 == board[yPos][xPos] ||
board[yPos][xPos + 1] - 1 == board[yPos][xPos])) {
pathRight = findSequence(xPos + 1, yPos);
}
ArrayList<Integer> ans;
if (pathDown.size() > pathRight.size()) {
ans = pathDown;
} else {
ans = pathRight;
}
ans.add(board[yPos][xPos]);
paths.get(yPos).set(xPos,ans);
pathLength[yPos][xPos] = ans.size();
return ans;
}
private void getSequence() {
ArrayList<Integer> result;
pathLength = new int[maxX + 1][maxY + 1];
paths = new ArrayList<ArrayList<ArrayList<Integer>>>();
for(int y = 0; y <= maxY; y++)
{
ArrayList<ArrayList<Integer>> line = new ArrayList<ArrayList<Integer>>();
for(int x = 0; x <= maxX; x++)
{
line.add(null);
pathLength[y][x] = -1;
}
paths.add(line);
}
result = findSequence(0, 0);
Collections.reverse(result);
for (int i = 0; i < result.size(); i++) {
System.out.println(result.get(i));
}
}
public static void main(String[] args) {
SnakeSequence sequence = new SnakeSequence();
sequence.getSequence();
}
}
Simple Recursive solution :
import java.util.ArrayList;
import java.util.List;
public class MaximumLengthSnakeSequence {
static int max = -1;
static List<Integer> maxListTemp = new ArrayList<>();
public static void main(String args[]) {
int count = 0;
int n = 4;
int m = 4;
int mat[][] = { { 9, 6, 5, 2 }, { 8, 7, 6, 5 }, { 7, 3, 1, 6 }, { 1, 1, 1, 7 }, };
List<Integer> maxList = new ArrayList<>();
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
List<Integer> list = new ArrayList<>();
list.add(mat[i][j]);
List<Integer> testList = recur(i, j, count, mat, n, m, list);
if (maxList.size() < testList.size()) {
maxList = new ArrayList<>(testList);
}
maxListTemp.clear();
}
}
System.out.println("max is " + maxList);
}
static List<Integer> recur(int i, int j, int count, int mat[][], int n, int m, List<Integer> list) {
int curData = mat[i][j];
int rightData = 0;
int downData = 0;
if (j + 1 < n && i < m) {
rightData = mat[i][j + 1];
if (Math.abs(curData - rightData) == 1) {
list.add(rightData);
recur(i, j + 1, count + 1, mat, n, m, list);
list.remove(list.size() - 1);
}
}
if (count > max) {
max = count;
}
if (maxListTemp.size() < list.size()) {
maxListTemp = new ArrayList<>(list);
}
if (i + 1 < m && j < n) {
downData = mat[i + 1][j];
if (Math.abs(curData - downData) == 1) {
list.add(downData);
recur(i + 1, j, count + 1, mat, n, m, list);
list.remove(list.size() - 1);
}
}
return maxListTemp;
}
}
I am trying to add the elements of two arrays with different lengths together.
The code below is only for the same length and here is all I have so far.
//for the same lengths
int[]num1 = {1,9,9,9};
int[]num2 = {7,9,9,9};// {9,9,9}
int total = 0, carry = 1;
int capacity = Math.max(num1.length,num2.length);
int []arraySum = new int [capacity];
for (int i = capacity - 1 ; i >= 0; i--)
{
arraySum[i] = num1[i]+ num2[i];
if (arraySum[i] > 9)
{
arraySum[i] = arraySum[i] % 10;
num2[i-1] = num2[i-1] + carry;
}
}
for(int i = 0; i < arraySum.length; i++)
{
System.out.print(arraySum[i]);
}
What should I do if I change the elements in num2 and length to like {9,9,9}?
I know I probably need to put another for-loop as an inside for-loop and control the indices of the array with smaller length but how....?? One more thing... what should I do for those for-loops conditions because num1 and num2 will be eventually INPUTED by the user.
Well, you can tell that the inputs are limited because if num1[0] + num2[0] > 9 the carry has no index to be placed, then it can't be compiled. So, I need to shift the whole array to the right and place the carry from num1[0] + num2[0]. Here is the problem!! Where should I put the shifting code? I am kinda confused.......
Actually, you declare an int[] array with the capacity as Math.max(num1.length, num2.length).
It is not encough. You should set the capacity as Math.max(num1.length, num2.length) +1.
Why?
See if num1 is {1,9,9,9} and num2 is {9,9,9,9}, how can the arraySum to represent the sum {1,1,9,9,8}?
So we need to declare it as below to consider if carry is needed.
int[] arraySum = new int[capacity + 1];
Then when print the sum, check if arraySum[0] is 0 or 1, if it euqals to 0, do not print it in Console.
Modified code for reference is as follows:
package question;
public class Example {
public static void main(String[] args) {
// for the same lengths
int[] num1 = { 1,9,9,9 };
int[] num2 = { 9,9,9,9};// {9,9,9}
// 1999+9999 = 11998, its length is greater than the max
int capacity = Math.max(num1.length, num2.length);
int[] arraySum = new int[capacity + 1];
int len2 = num2.length;
int len1 = num1.length;
if (len1 < len2) {
int lengthDiff = len2 - len1;
/*
* Flag for checking if carry is needed.
*/
boolean needCarry = false;
for (int i = len1 - 1; i >= 0; i--) {
/**
* Start with the biggest index
*/
int sumPerPosition =0;
if (needCarry) {
sumPerPosition = num1[i] + num2[i + lengthDiff] +1;
needCarry = false;
}else
{
sumPerPosition = num1[i] + num2[i + lengthDiff];
}
if (sumPerPosition > 9) {
arraySum[i + lengthDiff + 1] = sumPerPosition % 10;
needCarry = true;
}else
{
arraySum[i + lengthDiff + 1] = sumPerPosition % 10;
}
}
/**
* Handle the remaining part in nun2 Array
*/
for (int i = lengthDiff - 1; i >= 0; i--) {
/*
* Do not need to care num1 Array Here now
*/
if(needCarry){
arraySum[i + 1] = num2[i]+1;
}else
{
arraySum[i + 1] = num1[i] ;
}
if (arraySum[i + 1] > 9) {
arraySum[i + 1] = arraySum[i + 1] % 10;
needCarry = true;
} else {
needCarry = false;
}
}
/*
* Handle the last number, if carry is needed. set it to 1, else set
* it to 0
*/
if (needCarry) {
arraySum[0] = 1;
} else {
arraySum[0] = 0;
}
} else {
int lengthDiff = len1 - len2;
/*
* Flag for checking if carry is needed.
*/
boolean needCarry = false;
for (int i = len2 - 1; i >= 0; i--) {
/**
* Start with the biggest index
*/
int sumPerPosition = 0;
if (needCarry) {
sumPerPosition = num2[i] + num1[i + lengthDiff] +1;
needCarry = false;
}else
{
sumPerPosition = num2[i] + num1[i + lengthDiff];
}
if (sumPerPosition > 9) {
arraySum[i + lengthDiff + 1] = sumPerPosition % 10;
needCarry = true;
}else
{
arraySum[i + lengthDiff + 1] = sumPerPosition % 10;
}
}
/**
* Handle the remaining part in nun2 Array
*/
for (int i = lengthDiff - 1; i >= 0; i--) {
/*
* Do not need to care num1 Array Here now
*/
if(needCarry){
arraySum[i + 1] = num1[i]+1;
}else
{
arraySum[i + 1] = num1[i] ;
}
if (arraySum[i + 1] > 9) {
arraySum[i + 1] = arraySum[i + 1] % 10;
needCarry = true;
} else {
needCarry = false;
}
}
/*
* Handle the last number, if carry is needed. set it to 1, else set
* it to 0
*/
if (needCarry) {
arraySum[0] = 1;
} else {
arraySum[0] = 0;
}
}
/*
* Print sum
*
* if arraySum[0] ==1, print 1
*
* Do not print 0 when arraySum[0] ==0
*/
if(arraySum[0] == 1)
{
System.out.print(1);
}
for (int i = 1; i < arraySum.length; i++) {
System.out.print(arraySum[i]);
}
}
}
An example that num1 is {1,9,9,9} and num2 is {9,9,9,9}, the sum result is as follows:
output in Console:
11998
It's quite simpe actually. Inside your loop, check that the current index is valid for both array, and replace the value to add by 0 in case of an invalid index:
int value1 = (i < num1.length) ? num1[i] : 0;
int value2 = (i < num2.length) ? num2[i] : 0;
arraySum[i] = value1 + value2;
EDIT: I didn't understand that you wanted to right-align the arrays and not left-align them. The simplest solution is probably to write and read everything in the arrays in reverse order. So if the numbers are 456 and 7658, the first array would contain 6, 5, 4 and the second one would contain 8, 5, 6, 7.
Here is more performance centric solution:
public class SumArrays {
public static void main(String[] args) {
int[] num1 = {1, 9, 9, 9};
int[] num2 = {7, 9, 9, 9, 9, 9, 9};
int[] biggerArray = num1.length > num2.length ? num1 : num2;
int[] smallerArray = num1.length <= num2.length ? num1 : num2;
int[] summedArray = new int[biggerArray.length];
System.arraycopy(biggerArray, 0, summedArray, 0, biggerArray.length);
for (int i = 0; i < smallerArray.length; i++) {
summedArray[i] += smallerArray[i];
}
for (int i = 0; i < summedArray.length; i++) {
System.out.println(summedArray[i]);
}
}
}
You can use an ArrayList instead of an array and implement a normal addition method to it:
public static ArrayList<Integer> sum(int[] arr, int[] arr2) {
ArrayList<Integer> al = new ArrayList<>();
int i = arr.length - 1;
int j = arr2.length - 1;
int c = 0;
while (i >= 0 && j >= 0) {
int temp = arr[i] + arr2[j] + c;
if (temp >= 10) {
int r = temp % 10;
al.add(0, r);
c = temp / 10;
} else {
al.add(0, temp);
c = 0;
}
i--;
j--;
}
if (i < 0 && j >= 0) {
while (j >= 0) {
al.add(0, arr2[j] + c);
c = 0;
j--;
}
} else if (j < 0 && i >= 0) {
while (i >= 0) {
al.add(0, arr[i] + c);
c = 0;
i--;
}
} else
al.add(0, c);
return al;
}
for kicks, here is an alternative:
int[] num1 =
{ 1, 9, 9, 9 };
int[] num2 =
{ 7, 9, 9, 9 };
//convert the int array to a string
StringBuilder sb = new StringBuilder(num1.length);
for (int i : num1)
{
sb.append(i);
}
String sNum1 = sb.toString();
System.out.println(sNum1);
StringBuilder sb2 = new StringBuilder(num2.length);
for (int i : num2)
{
sb2.append(i);
}
String sNum2 = sb2.toString();
System.out.println(sNum2);
try
{
//parse the string to an int
int iNum1 = Integer.parseInt(sNum1);
int iNum2 = Integer.parseInt(sNum2);
//add them together
int sum = iNum1 + iNum2;
String sSum = Integer.toString(sum);
System.out.println(sSum);
// convert num back to array
int[] sumArray = new int[sSum.length()];
for (int i = 0; i < sSum.length(); i++)
{
sumArray[i] = sSum.charAt(i) - '0';
System.out.println(sumArray[i]);
}
}
catch (Exception e)
{
// couldnt parse ints
}