Colored Triangles Riddle - java

The riddle I'm hoping to solve can be found in this video.
In case you're unable to watch the whole video, here is an outline of the riddle:
In short, I need to find the smallest number of points where there is guaranteed to be at least one single-color triangle from any arrangement of red and blue lines connecting every single pair of points.
Below is a Java program I wrote to solve it.
public class RedBlueTimeTravelProblem {
public static void main(String[] args) {
int max = 3;
boolean alltri = false;
long start, end;
double time;
while (!alltri) {
start = System.nanoTime();
int edgecount = max * (max - 1) / 2;
int combos = (int) Math.pow(2, edgecount);
alltri = false;
for (int i=1; i<=combos; i++) {
int a = i-1;
int[][] edge = new int[edgecount][3];
for (int j=0; j<edgecount; j++) {
edge[j][2] = a % 2;
a = (a - a % 2) / 2;
}
int c2 = 0;
for (int j=1; j<=max; j++) {
for (int k=j+1; k<=max; k++) {
edge[c2][0] = j;
edge[c2][1] = k;
c2++;
}
}
boolean tri = false;
for (int j=0; j<edgecount-2; j++) {
for (int k=j+1; k<edgecount-1; k++) {
for (int m=k+1; m<edgecount; m++) {
if ((edge[j][0] == edge[k][0] && ((edge[j][1] == edge[m][0] && edge[k][1] == edge[m][1]) || (edge[j][1] == edge[m][1] && edge[k][1] == edge[m][0]))) ||
(edge[j][0] == edge[k][1] && ((edge[j][1] == edge[m][0] && edge[k][0] == edge[m][1]) || (edge[j][1] == edge[m][1] && edge[k][0] == edge[m][0]))) ||
(edge[j][0] == edge[m][0] && ((edge[j][1] == edge[k][0] && edge[k][1] == edge[m][1]) || (edge[j][1] == edge[k][1] && edge[k][0] == edge[m][1]))) ||
(edge[j][0] == edge[m][1] && ((edge[j][1] == edge[k][0] && edge[k][1] == edge[m][0]) || (edge[j][1] == edge[k][1] && edge[k][0] == edge[m][0])))) {
tri = tri || (edge[j][2] == edge[k][2] && edge[k][2] == edge[m][2]);
}
}
}
}
alltri = alltri && tri;
}
end = System.nanoTime();
time = (end - start) / 1000000000.0;
System.out.println(max + ": " + time + " seconds");
max++;
}
System.out.println("DONE");
}
}
It seems to work so far in checking whether 3, then 4, 5, etc. points will guarantee a single color triangle, but its time complexity is ridiculous. For example, here are the times for 3-8 points:
3: 1.8475E-5 seconds
4: 2.59876E-4 seconds
5: 0.009313668 seconds
6: 0.192455789 seconds
7: 19.226652708 seconds
Based on the trend, 8 points will take 30 min to 1 hr, 9 points will take roughly a few days, 10 will take over 6 months, and well... nobody wants to wait that long.
Any suggestions on how to make my program more time efficient?
Edit: Apparently the answer was 6. Not only was my code inefficient, it was just plain wrong. Also thanks for the suggestions everyone.

Related

Rat in a Maze using Stack (Java)

We are given a 2D character array and, starting from a given point, we have to find an 'exit', which is a '0' in the perimeter of the given matrix. The program returns true if a path is found, which will print a message when I run it in my main method later. We can only move up, left, down or right to a nearby '0'. I have already tried to make this work, however I get a memory error, which probably means I am stuck in an infinite loop. I have tried to implement this project using a Stack that I made using Nodes, which I also implemented myself using generics, like this: e.g.
StringStackImpl<int[][]> s = new StringStackImpl<>();
in a different .java file. These work fine. I tried to store the coordinates of a character in the matrix like this:
StringStackImpl<int[]> s = new StringStackImpl<>();
s.push(new int[]{i, j});
Here is my code:
private static boolean hasExit(char[][] maze, int n, int m, int i, int j) {
int d = -1;
boolean[][] visited = new boolean[n][m];
for (int a = 0; a < visited.length; a++) {
for (int b = 0; b < (visited[a]).length; b++) {
visited[a][b] = false;
}
}
StringStackImpl<int[]> s = new StringStackImpl<>();
s.push(new int[]{i, j});
while (!(s.isEmpty())) {
int[] temp = s.peek();
d += 1;
i = temp[0];
j = temp[1];
if (((i == 0) || (i == n-1) || (j == 0) || (j == m-1)) && (maze[i][j] == '0')) {
return true;
}
if (d == 0) {
if ((i-1 >= 0) && (maze[i-1][j] == '0') && !(visited[i-1][j])) {
visited[i-1][j] = true;
s.push(new int[]{i-1, j});
d = -1;
}
}
else if (d == 1) {
if ((j-1 >= 0) && (maze[i][j-1] == '0') && !(visited[i][j-1])) {
visited[i][j-1] = true;
s.push(new int[]{i, j-1});
d = -1;
}
}
else if (d == 2) {
if ((i+1 < n) && (maze[i+1][j] == '0') && !(visited[i+1][j])) {
visited[i+1][j] = true;
s.push(new int[]{i+1, j});
d = -1;
}
}
else if (d == 3) {
if ((j+1 < m) && (maze[i][j+1] == '0') && !(visited[i][j+1])) {
visited[i][j+1] = true;
s.push(new int[]{i, j+1});
d = -1;
}
}
else {
s.pop();
d = -1;
}
}
return false;
}
}
EDIT: It works now, thanks to kendavidson! <3
At first glance, and trusting you about StringStackImpl, I think you should only use d = -1;in the last else. Right now you are always setting it back to -1, and so you only check the d == 0 condition. From what I understood, d is the direction possible, and so should go from 0 to 3 ?
Other than that, take some time to give meaningfull names to your variables :
visited[i][j] is true when the node [i][j] has not been visited yet... That's counter-intuitive, and doesn't help others, or yourself, from understanding your code.
Are you forced to use char for i and j ? It really tends to make the code long and tedious for nothing.

Search and mark in two-dimensional Java-Array

I have an two-dimensional Array in Java. The values are like:
2 1 0 2 1
1 0 2 2 2
1 1 1 2 2
0 0 0 2 2
I want to change now all "2"s to a 1, but only the ones who are bordering the other ones. So after saying "Change (4|4) to "2", I want to have my Array like this:
2 1 0 1 1
1 0 1 1 1
1 1 1 1 1
0 0 0 1 1
What is the fastest way of doing this?
Assuming your working with a two dimensional array:
have int x and y set to 0
start from [x][y] on the array
Step 1: Check to if number equals "2"
If it does and:
0<x<4 and 0<y<4: Check [x-1][y-1],[x][y-1],[x][y+1],[x-1][y],[x+1][y], and [x+1][y+1]
x=0 and 0<y<3: Check [x][y-1],[x][y+1],[x+1][y], and [x+1][y+1]
x=4 and 0<y<3: Check [x-1][y-1],[x][y-1],[x][y+1],[x-1][y]
0<x<4 and y=0: Check [x][y+1],[x-1][y],[x+1][y], and [x+1][y+1]
0<x<4 and y=3: Check [x-1][y-1],[x][y-1],[x-1][y],[x+1][y]
and so on....
check to see if [x][y] = any of the checks
if so: store a 1 in [x][y] in an alternative array of the same size (lets call it flags)
iterate and repeat from step 1 until you have gone through the entire array
run through the flags and if the value is 1 at any address [x][y] change the corresponding value in our original array to "1".
sorry its a bit wordy and maybe confusing, please let me know if I need to clarify
Where is your code of what you've tried so far?
You could try using a nested for loop, then put if-else statements in the inner loop that
1. Will look at the values to the left and right of the index
2. If it is next to a 2 (and is a 2), will swap/assign the 2 in the index w/ a 1, otherwise it will just leave it alone.
Are you looking to make this scalable so that it would work with any array of 0s, 1s and 2s or just this exact array that you provided?
Please let me know if you need any clarification and include your code so that we can have something to work off of.
This example uses 2D array as array of arrays.
It generates 2D array with random numbers:
array[][] is the source array and all replacement routine will be performed in replaced[][] array (which is a copy of source one).
SIZE_I - number of rows and SIZE_J - number of columns;
RANGE_FROM - minimum number in array and RANGE_TO - maximum number;
SEARCH - number to search;
REPLACE - number to replace.
quantity - number or found and replaced items.
It prints source array to screen;
It checks source array for matching SEARCH constant with array item array[i][j]. Depending on item's location, several conditions should be met to replace this item in replaced[][] array and to increase quantity variable by one.
It prints replaced array to screen and number of replaced items.
I believe you can gain performance by using 2D array as one array.
For example your 2D array may be represented as {2,1,0,2,1,1,0,2,2,2,1,1,1,2,2,0,0,0,2,2} but you should handle it properly.
public static void main(String[] args) {
final int RANGE_FROM = 0;
final int RANGE_TO = 2;
final int SEARCH = 2;
final int REPLACE_TO = 1;
final int SIZE_I = 4;
final int SIZE_J = 5;
int quantity = 0;
int array[][] = new int[SIZE_I][SIZE_J];
int replaced[][] = new int[SIZE_I][SIZE_J];
// Generate arrays
for (int i = 0; i < SIZE_I; i++) {
for (int j = 0; j < SIZE_J; j++) {
array[i][j] = (int) (RANGE_FROM + Math.random() * (RANGE_TO - RANGE_FROM + 1));
replaced[i][j] = array[i][j];
}
}
// Display array
System.out.println("Source array:");
for (int x[]: array) {
for (int y: x) {
System.out.print(y + " ");
}
System.out.println();
}
System.out.println();
// Check array
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
if (i == 0 && j == 0) {
if (array[i][j] == SEARCH && (array[i+1][j] == SEARCH || array[i][j+1] == SEARCH)) {
quantity++;
replaced[i][j] = REPLACE_TO;
}
}
else if (i == 0 && j == array[i].length - 1) {
if (array[i][j] == SEARCH && (array[i+1][j] == SEARCH || array[i][j-1] == SEARCH)) {
quantity++;
replaced[i][j] = REPLACE_TO;
}
}
else if (i == array.length -1 && j == 0) {
if (array[i][j] == SEARCH && (array[i-1][j] == SEARCH || array[i][j+1] == SEARCH)) {
quantity++;
replaced[i][j] = REPLACE_TO;
}
}
else if (i == array.length -1 && j == array[i].length - 1) {
if (array[i][j] == SEARCH && (array[i][j-1] == SEARCH || array[i-1][j] == SEARCH)) {
quantity++;
replaced[i][j] = REPLACE_TO;
}
}
else if (i == 0 && j != 0 && j != array[i].length - 1) {
if (array[i][j] == SEARCH && (array[i][j-1] == SEARCH || array[i+1][j] == SEARCH || array[i][j+1] == SEARCH)) {
quantity++;
replaced[i][j] = REPLACE_TO;
}
}
else if (i != 0 && i != array.length - 1 && j == array[i].length - 1) {
if (array[i][j] == SEARCH && (array[i][j-1] == SEARCH || array[i-1][j] == SEARCH || array[i+1][j] == SEARCH)) {
quantity++;
replaced[i][j] = REPLACE_TO;
}
}
else if (i == array.length - 1 && j != 0 && j != array[i].length - 1) {
if (array[i][j] == SEARCH && (array[i][j-1] == SEARCH || array[i-1][j] == SEARCH || array[i][j+1] == SEARCH)) {
quantity++;
replaced[i][j] = REPLACE_TO;
}
}
else if (i != 0 && i != array.length - 1 && j == 0) {
if (array[i][j] == SEARCH && (array[i-1][j] == SEARCH || array[i][j+1] == SEARCH || array[i+1][j] == SEARCH)) {
quantity++;
replaced[i][j] = REPLACE_TO;
}
}
else {
if (array[i][j] == SEARCH && (array[i-1][j] == SEARCH || array[i][j+1] == SEARCH || array[i+1][j] == SEARCH || array[i][j-1] == SEARCH)) {
quantity++;
replaced[i][j] = REPLACE_TO;
}
}
}
}
// Display replaced array
System.out.println("Replaced array:");
for (int x[]: replaced) {
for (int y: x) {
System.out.print(y + " ");
}
System.out.println();
}
System.out.println();
System.out.println("Replace quantity: " + quantity);
System.out.println();
}

Infinite while loop inside of while loop java

So I'm having an issue simulating a game of craps. Everything runs properly except for the while loop within the while loop. When debugging, the sum variable is retaining it's value, the newSum variable is changing in every iteration, and often hitting 7 and the sum variable's value. If I comment out the nested while loop, and just have it as wins++;, then the code executes properly, to an expected value. So I'm quite certain the issue is within the nested loop.
Thanks for all your input!!
import java.util.Random;
import java.text.DecimalFormat;
public class Ch3Ex2
{
public static void main (String[] args)
{
Random rng = new Random();
int counter = 0;
int sum = 0;
int wins = 0;
int losses = 0;
int newSum = 0;
int reroll1 = 0;
int reroll2 = 0;
while (counter < 10000)
{
int die1 = rng.nextInt(6) + 1;
int die2 = rng.nextInt(6) + 1;
sum = die1 + die2;
if ((sum == 7) || (sum == 11))
wins++;
else if ((sum == 2) || (sum == 3) || (sum == 12))
losses++;
else
{
while((newSum != sum) || (newSum != 7))
{
reroll1 = rng.nextInt(6) + 1;
reroll2 = rng.nextInt(6) + 1;
newSum = reroll1 + reroll2;
}
if (newSum == sum)
{
wins++;
}
else
{
losses++;
}
}
counter++;
}
DecimalFormat percent = new DecimalFormat("0.00%");
double winDenom = wins + losses;
double winRate = wins/winDenom;
System.out.print("Your chance of winning a game of craps is : ");
System.out.println(percent.format(winRate));
}
}
The infinite loop is in this blook:
while((newSum != sum) || (newSum != 7))
{
reroll1 = rng.nextInt(6) + 1;
reroll2 = rng.nextInt(6) + 1;
newSum = reroll1 + reroll2;
}
because if you got a not 7 value in the first start, it will always be true and not stop.
i think you should replace the || with &&
so it could look like this:
while((newSum != sum) && (newSum != 7))
{
reroll1 = rng.nextInt(6) + 1;
reroll2 = rng.nextInt(6) + 1;
newSum = reroll1 + reroll2;
}
while((newSum != sum) || (newSum != 7))
This logic is incorrect. At the moment it will only ever exit if sum is equal to 7.
You need to use && not ||.
At your algorithm (newSum == sum) or (newSum == 7) conditions win so you will use the opposite of this action. After the logical negation
¬(x or y) = ¬x and ¬y
you will have this solution. That means you need to change your while condition as (newSum != sum) && (newSum != 7).
Your program will still be wrong as you never update newSum in nested while loop.
Once you enter the nested while loop, your newSum set to 7. Then it won't change anymore. Then this would cause your win chance to about 20%.
So you need update the newSum to 0 after a nested while loop terminate. Then the win change would be about 50%. You should
else
{
while((newSum != sum) || (newSum != 7))
{
reroll1 = rng.nextInt(6) + 1;
reroll2 = rng.nextInt(6) + 1;
newSum = reroll1 + reroll2;
}
if (newSum == sum)
{
wins++;
}
else
{
losses++;
}
newSum = 0;
}

Review an answer - Decode Ways

I'm trying to solve a question and my question here is why doesn't my solution work?. Here's the question and below's the answer.
Question taken from leetcode: http://oj.leetcode.com/problems/decode-ways/
A message containing letters from A-Z is being encoded to numbers using the following mapping:
'A' -> 1
'B' -> 2
...
'Z' -> 26
Given an encoded message containing digits, determine the total number of ways to decode it.
For example,Given encoded message "12", it could be decoded as "AB" (1 2) or "L" (12). The number of ways decoding "12" is 2.
My solution:
The point with my solution is going backwards and multiplying the number of options if a split is found. By split I mean that digits can be interpreted in two ways. For example: 11 can interpreted in two ways 'aa' or 'k'.
public class Solution {
public int numDecodings(String s) {
if (s.isEmpty() || s.charAt(0) == '0') return 0;
int decodings = 1;
boolean used = false; // Signifies that the prev was already use as a decimal
for (int index = s.length()-1 ; index > 0 ; index--) {
char curr = s.charAt(index);
char prev = s.charAt(index-1);
if (curr == '0') {
if (prev != '1' && prev != '2') {
return 0;
}
index--; // Skip prev because it is part of curr
used = false;
} else {
if (prev == '1' || (prev == '2' && curr <= '6')) {
decodings = decodings * 2;
if (used) {
decodings = decodings - 1;
}
used = true;
} else {
used = false;
}
}
}
return decodings;
}
}
The failure is on the following input:
Input:"4757562545844617494555774581341211511296816786586787755257741178599337186486723247528324612117156948"
Output: 3274568
Expected: 589824
This is a really interesting problem. First, I will show how I would solve this problem. We will see that it is not that complicated when using recursion, and that the problem can be solved using dynamic programming. We will produce a general solution that does not hardcode an upper limit of 26 for each code point.
A note on terminology: I will use the term code point (CP) not in the Unicode sense, but to refer to one of the code numbers 1 though 26. Each code point is represented as a variable number of characters. I will also use the terms encoded text (ET) and clear text (CT) in their obvious meanings. When talking about a sequence or array, the first element is called the head. The remaining elements are the tail.
Theoretical Prelude
The EC "" has one decoding: the CT "".
The EC "3" can be destructured into '3' + "", and has one decoding.
The EC "23" can be destructured as '2' + "3" or '23' + "". Each of the tails has one decoding, so the whole EC has two decodings.
The EC "123" can be destructured as '1' + "23" or '12' + "3". The tails have two and one decodings respectively. The whole EC has three decodings. The destructuring '123' + "" is not valid, because 123 > 26, our upper limit.
… and so on for ECs of any length.
So given a string like "123", we can obtain the number of decodings by finding all valid CPs at the beginning, and summing up the number of decodings of each tail.
The most difficult part of this is to find valid heads. We can get the maximal length of the head by looking at a string representation of the upper limit. In our case, the head can be up to two characters long. But not all heads of appropriate lengths are valid, because they have to be ≤ 26 as well.
Naive Recursive Implementation
Now we have done all the necessary work for a simple (but working) recursive implementation:
static final int upperLimit = 26;
static final int maxHeadSize = ("" + upperLimit).length();
static int numDecodings(String encodedText) {
// check base case for the recursion
if (encodedText.length() == 0) {
return 1;
}
// sum all tails
int sum = 0;
for (int headSize = 1; headSize <= maxHeadSize && headSize <= encodedText.length(); headSize++) {
String head = encodedText.substring(0, headSize);
String tail = encodedText.substring(headSize);
if (Integer.parseInt(head) > upperLimit) {
break;
}
sum += numDecodings(tail);
}
return sum;
}
Cached Recursive Implementation
Obviously this isn't very efficient, because (for longer ETs), the same tail will be analyzed multiple times. Also, we create a lot of temporary strings, but we'll let that be for now. One thing we can easily do is to memoize the number of decodings of a specific tail. For that, we use an array of the same length as the input string:
static final int upperLimit = 26;
static final int maxHeadSize = ("" + upperLimit).length();
static int numDecodings(String encodedText) {
return numDecodings(encodedText, new Integer[1 + encodedText.length()]);
}
static int numDecodings(String encodedText, Integer[] cache) {
// check base case for the recursion
if (encodedText.length() == 0) {
return 1;
}
// check if this tail is already known in the cache
if (cache[encodedText.length()] != null) {
return cache[encodedText.length()];
}
// cache miss -- sum all tails
int sum = 0;
for (int headSize = 1; headSize <= maxHeadSize && headSize <= encodedText.length(); headSize++) {
String head = encodedText.substring(0, headSize);
String tail = encodedText.substring(headSize);
if (Integer.parseInt(head) > upperLimit) {
break;
}
sum += numDecodings(tail, cache); // pass the cache through
}
// update the cache
cache[encodedText.length()] = sum;
return sum;
}
Note that we use an Integer[], not an int[]. This way, we can check for non-existent entries using a test for null. This solution is not only correct, it is also comfortably fast – naive recursion runs in O(number of decodings) time, while the memoized version runs in O(string length) time.
Towards a DP Solution
When you run above code in your head, you will notice that the first invocation with the whole string will have a cache miss, then calculate the number of decodings for the first tail, which also misses the cache every time. We can avoid this by evaluating the tails first, starting from the end of the input. Because all tails will have been evaluated before the whole string is, we can remove the checks for cache misses. Now we also don't have any reason for recursion, because all previous results are already in the cache.
static final int upperLimit = 26;
static final int maxHeadSize = ("" + upperLimit).length();
static int numDecodings(String encodedText) {
int[] cache = new int[encodedText.length() + 1];
// base case: the empty string at encodedText.length() is 1:
cache[encodedText.length()] = 1;
for (int position = encodedText.length() - 1; position >= 0; position--) {
// sum directly into the cache
for (int headSize = 1; headSize <= maxHeadSize && headSize + position <= encodedText.length(); headSize++) {
String head = encodedText.substring(position, position + headSize);
if (Integer.parseInt(head) > upperLimit) {
break;
}
cache[position] += cache[position + headSize];
}
}
return cache[0];
}
This algorithm could be optimized further by noticing that we only ever query the last maxHeadSize elements in the cache. So instead of an array, we could use a fixed-sized queue. At that point, we would have a dynamic programming solution that runs in *O(input length) time and O(maxHeadSize) space.
Specialization for upperLimit = 26
The above algorithms were kept as general as possible, but we can go and manually specialize it for a specific upperLimit. This can be useful because it allows us to do various optimizations. However, this introduces “magic numbers” that make the code harder to maintain. Such manual specializations should therefore be avoided in non-critical software (and the above algorithm is already as fast as it gets).
static int numDecodings(String encodedText) {
// initialize the cache
int[] cache = {1, 0, 0};
for (int position = encodedText.length() - 1; position >= 0; position--) {
// rotate the cache
cache[2] = cache[1];
cache[1] = cache[0];
cache[0] = 0;
// headSize == 1
if (position + 0 < encodedText.length()) {
char c = encodedText.charAt(position + 0);
// 1 .. 9
if ('1' <= c && c <= '9') {
cache[0] += cache[1];
}
}
// headSize == 2
if (position + 1 < encodedText.length()) {
char c1 = encodedText.charAt(position + 0);
char c2 = encodedText.charAt(position + 1);
// 10 .. 19
if ('1' == c1) {
cache[0] += cache[2];
}
// 20 .. 26
else if ('2' == c1 && '0' <= c2 && c2 <= '6') {
cache[0] += cache[2];
}
}
}
return cache[0];
}
Comparision with your code
The code is superficially similar. However, your parsing around characters is more convoluted. You have introduced a used variable that, if set, will decrement the decode count in order to account for double-character CPs. This is wrong, but I am not sure why. The main problem is that you are doubling the count at almost every step. As we have seen, the previous counts are added, and may very well be different.
This indicates that you wrote the code without proper preparation. You can write many kinds of software without having to think too much, but you can't do without careful analysis when designing an algorithm. For me, it is often helpful to design an algorithm on paper, and draw diagrams of each step (along the lines of the “Theoretical Prelude” of this answer). This is especially useful when you are thinking too much about the language you are going to implement in, and too little about possibly wrong assumptions.
I suggest that you read up on “proofs by induction” to understand how to write a correct recursive algorithm. Once you have a recursive solution, you can always translate it into an iterative version.
So here is some what simpler way out for your problem. This is pretty close to calculating Fibonacci, with the difference that there are condition checks on each smaller size subproblem.
The space complexity is O(1) and time is O(n)
The code is in C++.
int numDecodings(string s)
{
if( s.length() == 0 ) return 0;
int j = 0;
int p1 = (s[j] != '0' ? 1 : 0); // one step prev form j=1
int p2 = 1; // two step prev from j=1, empty
int p = p1;
for( int j = 1; j < s.length(); j++ )
{
p = 0;
if( s[j] != '0' )
p += p1;
if( isValidTwo(s, j-1, j) )
p += p2;
if( p==0 ) // no further decoding necessary,
break; // as the prefix 0--j is has no possible decoding.
p2 = p1; // update prev for next j+1;
p1 = p;
}
return p;
}
bool isValidTwo(string &s, int i, int j)
{
int val= 10*(s[i]-'0')+s[j]-'0';
if ( val <= 9 )
return false;
if ( val > 26 )
return false;
return true;
}
Here is my code to solve the problem. I use DP , I think it's clear to understand.
Written in Java
public class Solution {
public int numDecodings(String s) {
if(s == null || s.length() == 0){
return 0;
}
int n = s.length();
int[] dp = new int[n+1];
dp[0] = 1;
dp[1] = s.charAt(0) != '0' ? 1 : 0;
for(int i = 2; i <= n; i++){
int first = Integer.valueOf(s.substring(i-1,i));
int second = Integer.valueOf(s.substring(i-2,i));
if(first >= 1 && first <= 9){
dp[i] += dp[i-1];
}
if(second >= 10 && second <= 26){
dp[i] += dp[i-2];
}
}
return dp[n];
}
}
Since I struggled with this problem myself, here is my solution and reasoning. Probably I will mostly repeat what amon wrote, but maybe someone will find it helpful. Also it's c# not java.
Let's say that we have input "12131" and want to obtain all possible decoded strings.
Straightforward recursive solution would do iterate from left to right, obtain valid 1 and 2 digits heads, and invoke function recursively for tail.
We can visualize it using a tree:
There are 5 leaves and this is number of all possible decoded strings. There are also 3 empty leaves, because number 31 cannot be decoded into letter, so these leaves are invalid.
Algorithm might look like this:
public IList<string> Decode(string s)
{
var result = new List<string>();
if (s.Length <= 2)
{
if (s.Length == 1)
{
if (s[0] != '0')
result.Add(this.ToASCII(s));
}
else if (s.Length == 2)
{
if (s[0] != '0' && s[1] != '0')
result.Add(this.ToASCII(s.Substring(0, 1)) + this.ToASCII(s.Substring(1, 1)));
if (s[0] != '0' && int.Parse(s) > 0 && int.Parse(s) <= 26)
result.Add(this.ToASCII(s));
}
}
else
{
for (int i = 1; i <= 2; ++i)
{
string head = s.Substring(0, i);
if (head[0] != '0' && int.Parse(head) > 0 && int.Parse(head) <= 26)
{
var tails = this.Decode(s.Substring(i));
foreach (var tail in tails)
result.Add(this.ToASCII(head) + tail);
}
}
}
return result;
}
public string ToASCII(string str)
{
int number = int.Parse(str);
int asciiChar = number + 65 - 1; // A in ASCII = 65
return ((char)asciiChar).ToString();
}
We have to take care of numbers starting with 0 ("0", "03", etc.), and greater than 26.
Because in this problem we need only count decoding ways, and not actual strings, we can simplify this code:
public int DecodeCount(string s)
{
int count = 0;
if (s.Length <= 2)
{
if (s.Length == 1)
{
if (s[0] != '0')
count++;
}
else if (s.Length == 2)
{
if (s[0] != '0' && s[1] != '0')
count++;
if (s[0] != '0' && int.Parse(s) > 0 && int.Parse(s) <= 26)
count++;
}
}
else
{
for (int i = 1; i <= 2; ++i)
{
string head = s.Substring(0, i);
if (head[0] != '0' && int.Parse(head) > 0 && int.Parse(head) <= 26)
count += this.DecodeCount(s.Substring(i));
}
}
return count;
}
The problem with this algorithm is that we compute results for the same input string multiple times. For example there are 3 nodes ending with 31: ABA31, AU31, LA31. Also there are 2 nodes ending with 131: AB131, L131.
We know that if node ends with 31 it has only one child, since 31 can be decoded only in one way to CA. Likewise, we know that if string ends with 131 it has 2 children, because 131 can be decoded into ACA or LA. Thus, instead of computing it all over again we can cache it in map, where key is string (eg: "131"), and value is number of decoded ways:
public int DecodeCountCached(string s, Dictionary<string, int> cache)
{
if (cache.ContainsKey(s))
return cache[s];
int count = 0;
if (s.Length <= 2)
{
if (s.Length == 1)
{
if (s[0] != '0')
count++;
}
else if (s.Length == 2)
{
if (s[0] != '0' && s[1] != '0')
count++;
if (s[0] != '0' && int.Parse(s) > 0 && int.Parse(s) <= 26)
count++;
}
}
else
{
for (int i = 1; i <= 2; ++i)
{
string head = s.Substring(0, i);
if (head[0] != '0' && int.Parse(head) > 0 && int.Parse(head) <= 26)
count += this.DecodeCountCached(s.Substring(i), cache);
}
}
cache[s] = count;
return count;
}
We can refine this even further. Instead of using strings as a keys, we can use length, because what is cached is always tail of input string. So instead of caching strings: "1", "31", "131", "2131", "12131" we can cache lengths of tails: 1, 2, 3, 4, 5:
public int DecodeCountDPTopDown(string s, Dictionary<int, int> cache)
{
if (cache.ContainsKey(s.Length))
return cache[s.Length];
int count = 0;
if (s.Length <= 2)
{
if (s.Length == 1)
{
if (s[0] != '0')
count++;
}
else if (s.Length == 2)
{
if (s[0] != '0' && s[1] != '0')
count++;
if (s[0] != '0' && int.Parse(s) > 0 && int.Parse(s) <= 26)
count++;
}
}
else
{
for (int i = 1; i <= 2; ++i)
{
string head = s.Substring(0, i);
if (s[0] != '0' && int.Parse(head) > 0 && int.Parse(head) <= 26)
count += this.DecodeCountDPTopDown(s.Substring(i), cache);
}
}
cache[s.Length] = count;
return count;
}
This is recursive top-down dynamic programming approach. We start from the begining, and then recursively compute solutions for tails, and memoize those results for further use.
We can translate it to bottom-up iterative DP solution. We start from the end and cache results for tiles like in previous solution. Instead of map we can use array because keys are integers:
public int DecodeCountBottomUp(string s)
{
int[] chache = new int[s.Length + 1];
chache[0] = 0; // for empty string;
for (int i = 1; i <= s.Length; ++i)
{
string tail = s.Substring(s.Length - i, i);
if (tail.Length == 1)
{
if (tail[0] != '0')
chache[i]++;
}
else if (tail.Length == 2)
{
if (tail[0] != '0' && tail[1] != '0')
chache[i]++;
if (tail[0] != '0' && int.Parse(tail) > 0 && int.Parse(tail) <= 26)
chache[i]++;
}
else
{
if (tail[0] != '0')
chache[i] += chache[i - 1];
if (tail[0] != '0' && int.Parse(tail.Substring(0, 2)) > 0 && int.Parse(tail.Substring(0, 2)) <= 26)
chache[i] += chache[i - 2];
}
}
return chache.Last();
}
Some people simplify it even further, initializing cache[0] with value 1, so they can get rid of conditions for tail.Length==1 and tail.Length==2. For me it is unintuitive trick though, since clearly for empty string there is 0 decode ways not 1, so in such case additional condition must be added to handle empty input:
public int DecodeCountBottomUp2(string s)
{
if (s.Length == 0)
return 0;
int[] chache = new int[s.Length + 1];
chache[0] = 1;
chache[1] = s.Last() != '0' ? 1 : 0;
for (int i = 2; i <= s.Length; ++i)
{
string tail = s.Substring(s.Length - i, i);
if (tail[0] != '0')
chache[i] += chache[i - 1];
if (tail[0] != '0' && int.Parse(tail.Substring(0, 2)) > 0 && int.Parse(tail.Substring(0, 2)) <= 26)
chache[i] += chache[i - 2];
}
return chache.Last();
}
My solution is based on the idea that the arrangement of items(char/digit) within a particular substring is completely independent of the same within a different substring.
So we need to multiply each of those independent ways to get the total number of ways.
// nc is the number of consecutive 1's or 2's in a substring.
// Returns the number of ways these can be arranged within
// themselves to a valid expr.
int ways(int nc){
int n = pow(2, (nc/2)); //this part can be memorized using map for optimization
int m = n;
if (nc%2) {
m *= 2;
}
return n + m - 1;
}
bool validTens(string A, int i){
return (A[i] == '1' || (A[i] == '2' && A[i+1] <= '6'));
}
int numDecodings(string A) {
int ans = 1;
int nc;
if ((A.length() == 0)||(A[0] == '0')) return 0;
for(int i = 1; i < A.length();i++){
if(A[i] == '0' && validTens(A, i-1) == false) return 0; //invalid string
while(i < A.length() && validTens(A, i-1)) {
if(A[i] == '0'){
//think of '110' or '1210', the last two digits must be together
if(nc > 0) nc--;
}
else nc++;
i++;
}
ans *= ways(nc);
nc = 0;
}
return ans;
}
Java solution with space and time complexity O(n)
public int numDecodings(String s) {
int n = s.length();
if (n > 0 && s.charAt(0) == '0')
return 0;
int[] d = new int[n + 1];
d[0] = 1;
d[1] = s.charAt(0) != '0' ? 1 : 0;
for (int i = 2; i <= n; i++) {
if (s.charAt(i - 1) > '0')
d[i] = d[i] + d[i - 1];
if (s.charAt(i - 2) == '2' && s.charAt(i - 1) < '7')
d[i] = d[i - 2] + d[i];
if (s.charAt(i - 2) == '1' && s.charAt(i - 1) <= '9')
d[i] = d[i - 2] + d[i];
}
return d[n];
}
Here is an O(N) C++ DP implementation.
int numDecodings(string s) {
if(s[0] == '0') return 0; // Invalid Input
int n = s.length();
// dp[i] denotes the number of ways to decode the string of length 0 to i
vector<int> dp(n+1, 0);
// base case : string of 0 or 1 characters will have only 1 way to decode
dp[0] = dp[1] = 1;
for(int i = 2; i <= n; i++) {
// considering the previous number
if(s[i-1] > '0') dp[i] += dp[i-1];
// considering the previous two numbers
if(s[i-2] == '1' || (s[i-2] == '2' && s[i-1] < '7')) dp[i] += dp[i-2];
}
return dp[n];
}

Changing value in rectangular two dimensional array

I am creating a very rudimentary battle ship program for fun and I
have come across a problem with changing values in my rectangular two dimensional array. Essentially, there are five "ships" placed in this two dimensional array. A user then inputs an integer to see if they hit or miss. I wanted the output to display the array with the guessed number represented with an X for a miss and a 0 for a hit. I am having trouble with changing the value at area[i][j] when it equals the value of t to the 0 or X. I am new to java so I am trying to learn. Any help would be appreciated. Thank you in advance.
import java.util.*;
public class battleship {
//Rudimentary Battleship game
public static void main(String[] args) {
System.out.println(" Hello and welcome to a basic version of Battleship.");
System.out.println(" The objective of this game is to sink all five ships located on the grid listed below.");
System.out.println(" Follow the prompt located underneath the grid ");
final int ROWS = 10;
final int COLS = 10;
int sum = 0;
int [][] area = { {1,2,3,4,5,6,7,8,9,10},
{11,12,13,14,15,16,17,18,19,20},
{21,22,23,24,25,26,27,28,29,30},
{31,32,33,34,35,36,37,38,39,40},
{41,42,43,44,45,46,47,48,49,50},
{51,52,53,54,55,56,57,58,59,60},
{61,62,63,64,65,66,67,68,69,70},
{71,72,73,74,75,76,77,78,79,80},
{81,82,83,84,85,86,87,88,89,90},
{91,92,93,94,95,96,97,98,99,100} };
for(int i=0; i < area.length; i++) {
for (int j=0; j < area[i].length; j++) {
if(area[i][j] < 10)
System.out.print(" "+(area[i][j])+" ");
else if(area[i][j] < 100)
System.out.print(" "+(area[i][j])+" ");
else
System.out.print(" "+(area[i][j])+" ");
}
System.out.println("");
}
Scanner input = new Scanner(System.in);{
System.out.println("Enter attack integer:");
int t;
while(true){
t = input.nextInt();
if ((t == 41) || (t == 42) || (t == 43)){
System.out.println("Hit - Destroyer");}
if ((t == 80) || (t == 90) || (t == 100)){
System.out.println("Hit - Submarine");}
if((t == 52) || (t == 62) || (t== 72) || (t == 82) || (t == 92)){
System.out.println ("Hit - Aircraft Carrier");}
if((t == 15) || (t == 16) || (t == 17) || (t == 18)){
System.out.println ("Hit - Battleship");}
if((t == 1) || (t == 2)){
System.out.println ("Hit - PT Boat");}
else{
System.out.println ("Miss");
}
System.out.println("You have fired at:" + t);
int w = 0;
for(int i=0; i < area.length; i++) {
for (int j=0; j < area[i].length; j++) {
if (area[i][j] == t)
if(area[i][j] < 10)
System.out.print(" "+(area[i][j])+" ");
else if(area[i][j] < 100)
System.out.print(" "+(area[i][j])+" ");
else
System.out.print(" "+(area[i][j])+" ");
}
System.out.println("");
}
}
}
}
}
You would be much better off using object orientation. Here's a skeleton to get you started:
public class Cell {
private boolean ship = false;
private boolean hit = false;
public boolean getHit() {
return hit;
}
public void setHit(boolean hit) {
this.hit = hit;
}
}
Then
public class Board() {
public static final int SIZE = 10;
private Cell[][] board;
public Board() {
board = new Cell[SIZE][SIZE];
for(int i = 0; i < 10; i++)
for(int j = 0; j < 10; j++)
board[i][j] = new Cell();
public boolean getHit(int x, int y) {
if(x < 0 || x >= SIZE || y < 0 || y >= SIZE) throw new BattleshipException("You queried a cell that doesn't exist!");
return board[x][y].getHit();
}
// etc etc.
}
Flesh out all the methods you need, add fields when you need them, this will work much better!
This is simple, modifying your code:
if ((t == 41) || (t == 42) || (t == 43)){
System.out.println("Hit - Destroyer");
area[i][j] = "X";
}
if ((t == 80) || (t == 90) || (t == 100)){
System.out.println("Hit - Submarine");
area[i][j] = "X";
}
if((t == 52) || (t == 62) || (t== 72) || (t == 82) || (t == 92)){
System.out.println ("Hit - Aircraft Carrier");
area[i][j] = "X";
}
if((t == 15) || (t == 16) || (t == 17) || (t == 18)){
System.out.println ("Hit - Battleship");
area[i][j] = "X";
}
if((t == 1) || (t == 2)){
System.out.println ("Hit - PT Boat");
area[i][j] = "X";
}
else{
System.out.println ("Miss");
area[i][j] = "O";
}
A note: be careful, arrays are 0 indexed, so your ship positions don't exactly correspond to the way you set up the grid. i.e. area[4][1] is not "41". You seemed to do this correctly (in the case of the destroyer) for the columns but not the rows. The if statements should check for 31, 32, 33 if the ship is at area[4][0], area[4][1], area [4][2]. In fact, you might be better off labeling the positions from 00, 01, 02, ... , 97, 98, 99 so that the indices correspond to the numbers.

Categories

Resources