Recursive method for Pascal's triangle - java

I have written a method to evaluate a Pascal's triangle of n rows. However when I test the method I receive the error:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
Here is the code:
public static int[] PascalTriangle(int n) {
int[] pt = new int[n + 1];
if (n == 0) {
pt[0] = 1;
return pt;
}
int[] ppt = PascalTriangle(n - 1);
pt[0] = pt[n] = 1;
for (int i = 0; i < ppt.length; i++) {
pt[i] = ppt[i - 1] + ppt[i];
}
return pt;
}
Please let me know if you have any ideas for how the code could be edited to fix the problem.

for(int i = 0; i < ppt.length; i++)
{
pt[i] = ppt[i-1] + ppt[i];
In your first iteration, i == 0 and so (i-1) == -1. This is the cause of the error.
You can special handle the boundaries to avoid this. Or as the others have suggested, start i at 1 instead of 0.

Here is some code a friend of mine came up with
import java.util.Scanner;
public class Pascal {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter the number of rows to print: ");
int rows = scanner.nextInt();
System.out.println("Pascal Triangle:");
print(rows);
scanner.close();
}
public static void print(int n) {
for (int i = 0; i < n; i++) {
for (int k = 0; k < n - i; k++) {
System.out.print(" "); // print space for triangle like structure
}
for (int j = 0; j <= i; j++) {
System.out.print(pascal(i, j) + " ");
}
System.out.println();
}
}
public static int pascal(int i, int j) {
if (j == 0 || j == i) {
return 1;
} else {
return pascal(i - 1, j - 1) + pascal(i - 1, j);
}
}
}

In this code:
pt[0] = pt[n] = 1;
for(int i = 0; i < ppt.length; i++)
{
pt[i] = ppt[i-1] + ppt[i];
}
the problem is that when i is 0, you're trying to access ppt[i-1] which is ppt[-1]. The thing to notice is that when i is 0, you don't need to execute the statement that sets pt[i], because you already set pt[0] up before the loop! Try initializing i to 1 instead of 0.

Improvement in #Clemson code using Dynamic Programming :
class Solution {
int[][] dp ;
public List<List<Integer>> generate(int numRows) {
dp = new int[numRows][numRows];
List<List<Integer>> results = new ArrayList<>();
pascal(results, numRows);
return results;
}
private void pascal(List<List<Integer>> results, int numRows) {
for(int i = 0; i < numRows; i++) {
List<Integer> list = new ArrayList<>();
for(int j = 0; j <= i ; j++) {
list.add(dfs(i, j));
}
results.add(list);
}
}
private int dfs(int i, int j) {
if(j == 0 || i == j) return 1;
if(dp[i][j] != 0) return dp[i][j];
return dp[i][j] = dfs(i - 1, j - 1) + dfs(i - 1, j );
}
}

This isn't the solution to your code but it is solution to printing Pascals Triangle using only recursion which means no loops, using the combinations formula. All it needs is a main method or demo class to create an instance of the PascalsTriangle class. Hope this helps future Java students.
public class PascalsTriangle {
private StringBuilder str; // StringBuilder to display triangle
/**
* Starts the process of printing the Pascals Triangle
* #param rows Number of rows to print
*/
public PascalsTriangle(int rows) {
str = new StringBuilder();
printTriangle(rows, str);
}
/**
* Uses recursion to function as an "outer loop" and calls
* itself once for each row in triangle. Then displays the result
* #param row The number of the row to generate
* #param str StringBuilder to insert each row into
*/
public static void printTriangle(int row, StringBuilder str) {
// calls itself until row equals -1
if (row >= 0) {
// calls lower function to generate row and inserts the result into front of StringBuilder
str.insert(0, getRow(row, 0) + "\n");
// calls itself with a decremented row number
printTriangle(row - 1, str);
} else {
// when the base case is reached - display the result
JOptionPane.showMessageDialog(null, str);
System.exit(0);
}
}
/**
* Uses recursion to act as the "inner loop" and calculate each number in the given row
* #param rowNumber Number of the row being generated
* #param elementNumber Number of the element within the row (always starts with 0)
* #return String containing full row of numbers or empty string when base case is reached
*/
public static String getRow(int rowNumber, int elementNumber) {
// calls itself until elementNumber is greater than rowNumber
if (elementNumber <= rowNumber) {
// calculates element using combinations formula: n!/r!(n-r)!
int element = fact(rowNumber) / (fact(elementNumber) * (fact(rowNumber - elementNumber)));
// calls itself for each element in row and returns full String
return element + " " + getRow(rowNumber, elementNumber + 1);
} else return "";
}
/**
* Helper function that uses recursion to calculate factorial of given integer
* #param n Number to calculate factorial
* #return Factorial
*/
public static int fact(int n) {
if (n <= 0)
return 1;
else
return n * fact(n - 1);
}

Related

Adding all solutions of N Queen Problem to an arrayList

The n-queens puzzle is the problem of placing n queens on a (n×n) chessboard such that no two queens can attack each other.
I used backtracking to solve the problem. But I ran in a strange issue.
Below is the code I wrote:
import java.util.ArrayList;
public class NQueenProblem {
static ArrayList<ArrayList<Integer>> ans = new ArrayList<ArrayList<Integer>>();
static ArrayList<ArrayList<Integer>> nQueen(int n) {
ArrayList<Integer> positions = new ArrayList<Integer>();
//boolean[][] placed = new boolean[n][n];
solveNQueenRec(n, 0, new boolean[n][n], positions);
//for dubugging purpose. This prints empty arrays. not able to understand why?
for (ArrayList<Integer> list : ans)
System.out.println(list);
return ans;
}
static void solveNQueenRec(int n, int col, boolean[][] placed, ArrayList<Integer> positions) {
if (col == n) {
//for debugging process
System.out.println("Adding " + positions);
ans.add(positions);
System.out.println("Added " + positions);
}
for (int row = 0; row < n && col < n; row++) {
if (isSafe(row, col, placed, n)) {
placed[row][col] = true;
positions.add(row + 1);
solveNQueenRec(n, col + 1, placed, positions);
placed[row][col] = false;
positions.remove(positions.size() - 1);
}
}
// return null;
}
private static boolean isSafe(int row, int col, boolean[][] placed, int n) {
boolean safe = true;
// checking if exists in same row
for (int i = 0; i < col; i++) {
if (placed[row][i])
safe = false;
}
// checking for upper diagonal
for (int i = row, j = col; i >= 0 && j >= 0; i--, j--) {
if (placed[i][j])
safe = false;
}
// checking for lower diagonal
for (int i = row, j = col; i < n && j >= 0; i++, j--) {
if (placed[i][j])
safe = false;
}
return safe;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
nQueen(4);
}
}
What I am not able to understand is why my ans is empty when I could see in logs list being added to my ans. Am I doing some silly mistake. Please help me with the issue. If possible please help me with links to understand the issue and how could I avoid these issues in future.
I think you believe that when the JVM executes
ans.add(positions);
that it is taking a copy of the current state of positions and adding it to the list. It isn't, it is doing exactly what the code says: adding a reference to an ArrayList to ans.
All the items in ans are references to the same ArrayList, and that array list is empty when you print out ans.
ans.add(positions);
is your "problem". You are just adding to ans a reference to the list positions. Thus ans is full of references to the same positions list. As you modify positions several time after insertion, until emptied it, at the end ans is full of n references (n being the number of solutions found) the same positions list that is empty at the end.
What you need is to insert a copy of the current positions list at the time a solution is found:
ans.add(new ArrayList<Integer>(positions));
copy is obtained by constructing a new list with the exact content of the original one.
Add this checking make sure it is greater than zero, then print out the answer. Moreover, add the print queen function, your will see the answer clearly. If this solve your problem, please make as answer.
for (ArrayList<Integer> list : ans) {
if (list.size() > 0)
System.out.println(list);
}
static void solveNQueenRec(int n, int col, boolean[][] placed, ArrayList<Integer> positions) {
if (col == n)
printQueens(positions);
for (int row = 0; row < n && col < n; row++) {
if (isSafe(row, col, placed, n)) {
placed[row][col] = true;
positions.add(row + 1);
solveNQueenRec(n, col + 1, placed, positions);
placed[row][col] = false;
positions.remove(positions.size() - 1);
}
}
// return null;
}
public static void printQueens(ArrayList<Integer> q) {
int n = q.size();
for (int i = 0; i < n; i++) {
for (int j = 1; j <= n; j++) {
if (q.get(i) == j)
System.out.print("Q ");
else
System.out.print("* ");
}
System.out.println();
}
System.out.println();
}
solution to 4-queens sample:
* Q * *
* * * Q
Q * * *
* * Q *
* * Q *
Q * * *
* * * Q
* Q * *

How do I use a void method without printing?

I am having problems understanding a program I need to write for class. My program works as it should, but the problem states that it wants me to not print within my methods. I am confused on how I should output my values because my methods must be void according to the problem (which doesn't return anything) and I can't print inside of them.
Here is the question:
Design and implement a java program (name it ArrayMethods), that defines 4 methods as follows:
int arrayMax (int[] array)
int arrayMin (int[] array)
void arraySquared (int[] array)
void arrayReverse (int[] array)
Test your methods by creating an array of length 5 within your main method and filling it with random numbers between 1 and 1000. Your program should then display the original array, display the smallest number in the array, display the greatest number in the array, display the revered array, and display the square of each value in the array. You main method shoudl invoke each method exactly once, with each invocation use the original array as the actual parameter. No printing within the methods. Document your code, and organize/space your outputs properly. Use escape characters and formatting objects when applicable.
So again my question is: How do I use those methods without printing anything if I can't return a value? If anyone could give me a clue on how to solve this, it would be greatly appreciated.
import java.util.Random;
import java.util.Arrays;
public class ArrayMethods
{
public static void main (String[] args)
{
int[] array = new int[5];
array[0] = (int)(Math.random() * (1000 - 1)) + 1;
array[1] = (int)(Math.random() * (1000 - 1)) + 1;
array[2] = (int)(Math.random() * (1000 - 1)) + 1;
array[3] = (int)(Math.random() * (1000 - 1)) + 1;
array[4] = (int)(Math.random() * (1000 - 1)) + 1;
System.out.println("The values within the array are: " + Arrays.toString(array));
System.out.println("The maximum value within the array is: " + arrayMax(array));
System.out.println("The minimum value of the array is: " + arrayMin(array));
System.out.print("The values within the array (squared) are: ");
arraySquared(array);
System.out.print("\n");
System.out.print("The array reversed is: ");
arrayReverse(array);
}
public static int arrayMax (int[] array)
{
int max = 0;
for (int i = 0; i < array.length; i++)
{
if (array[i] > max)
{
max = array[i];
}
}
return max;
}
public static int arrayMin (int[] array)
{
int min = 1000;
for (int i = 0; i < array.length; i++)
{
if (array[i] < min)
{
min = array[i];
}
}
return min;
}
public static void arraySquared (int[] array)
{
int[] array2 = new int[5];
for (int i = 0; i < array.length; i++)
{
array2[i] = array[i] * array[i];
System.out.print(array2[i]);
while ( i < array.length - 1)
{
System.out.print(", ");
break;
}
}
}
public static void arrayReverse (int[] array)
{
for(int i = array.length - 1; i >= 0; i--)
{
System.out.print(array[i] + " ");
}
}
}
For the void functions, in C# you'd simply use an "out" or "ref" keyword. If they insist on Java, Java doesn't have an equivalent, but you can do something like that with an object.
public class PassArray
{
public int[] array;
public PassArray(int[] array)
{
this.array = array;
}
}
public static void Reverse(PassArray arrayHolder)
{
int[] reversed = new int[arrayHolder.array.Length];
int j = 0;
for (int i = arrayHolder.array.Length - 1; i >= 0; i--)
{
reversed[j] = arrayHolder.array[i];
j++;
}
arrayHolder.array = reversed;
}
static void Main(string[] args)
{
int[] toReverse = new[] { 10, 30, 2, 1, 3, 100, 340 };
PassArray passedObject = new PassArray(toReverse);
Reverse(passedObject);
// passedObject.array will now have the reversed array
}
Obviously this could can be improved but it should at least give the right idea.

Subset sum negative values

I was wondering how to work with negative values and a negative target, right now my program gives index out of bounds errors whenever negative values are given to these variables. I need my hasSum function work with negative values for this project, I can't just assume positive.
import java.util.Stack;
import java.util.Scanner;
public class subsetSum {
static Scanner input = new Scanner(System.in);
static {
System.out.print("Enter the target (T)" + "\n");
}
/** Set a value for target sum */
static int TARGET_SUM = input.nextInt(); //this is the target
/** Store the sum of current elements stored in stack */
static int sumInStack = 0;
Stack<Integer> stack = new Stack<Integer>();
public static void main(String[] args) {
//the size is S
System.out.println("\n" + "Enter the size of the set (S)");
int values = input.nextInt(); //size = "values"
//value of each size entry
System.out.println("\n" + "Enter the value of each entry for S");
int [] numbers = new int[values];
for(int i = 0; i < values; i++) //for reading array
{
numbers[i] = input.nextInt();
}
if(hasSum(numbers, TARGET_SUM)){
System.out.println("\n" + "Can: ");
subsetSum get = new subsetSum(); // encapsulation
get.populateSubset(numbers, 0, numbers.length);
}else{
System.out.println("\n" + "Cannot");
}
}
//method based on dynamic programming O(sum*length)
public static boolean hasSum(int [] array, int sum)
{
int i;
int len = array.length;
boolean[][] table = new boolean[sum + 1][len + 1]; //this has to be changed for negative
//If sum is zero; empty subset always has a sum 0; hence true
for(i = 0; i <= len; i++){
table[0][i] = true;
}
//If set is empty; no way to find the subset with non zero sum; hence false
for(i = 1; i <= sum; i++){
table[i][0] = false;
}
//calculate the table entries in terms of previous values
for(i = 1; i <= sum; i++)
{
for(int j = 1; j <= len; j++)
{
table[i][j] = table[i][j - 1];
if(!table[i][j] && i >= array[j - 1]){
table[i][j] = table[i - array[j - 1]][j - 1];
}
}
}
return table[sum][len]; //this has to be changed for negative
}
public void populateSubset(int[] data, int fromIndex, int endIndex) {
/*
* Check if sum of elements stored in Stack is equal to the expected
* target sum.
*
* If so, call print method to print the candidate satisfied result.
*/
if (sumInStack >= TARGET_SUM) {
if (sumInStack == TARGET_SUM) {
print(stack);
}
// there is no need to continue when we have an answer
// because nothing we add from here on in will make it
// add to anything less than what we have...
return;
}
for (int currentIndex = fromIndex; currentIndex < endIndex; currentIndex++) {
if (sumInStack + data[currentIndex] <= TARGET_SUM) {
stack.push(data[currentIndex]);
sumInStack += data[currentIndex];
/*
* Make the currentIndex +1, and then use recursion to proceed
* further.
*/
populateSubset(data, currentIndex + 1, endIndex);
sumInStack -= (Integer) stack.pop();
}
}
}
/**
* Print satisfied result. i.e. 5 = 1, 4
*/
private void print(Stack<Integer> stack) {
StringBuilder sb = new StringBuilder();
for (Integer i : stack) {
sb.append(i).append(",");
}
// .deleteCharAt(sb.length() - 1)
System.out.println(sb.deleteCharAt(sb.length() - 1).toString());
}
}
Are you trying to find a sum of subset or a subarray?
If a subset, then a simple recursion could do the trick, e.g.:
public static boolean hasSum(int [] array, int sum)
{
return hasSum(array, 0, 0, sum);
}
private static boolean hasSum(int[] array, int index, int currentSum, int targetSum) {
if (currentSum == targetSum)
return true;
if (index == array.length)
return false;
return hasSum(array, index + 1, currentSum + array[index], targetSum) || // this recursion branch includes current element
hasSum(array, index + 1, currentSum, targetSum); // this doesn't
}
If you're trying to find a subarray, I'd use prefix sums, e.g.:
public static boolean hasSum(int [] array, int sum)
{
int[] prefixSums = new int[array.length];
for (int i = 0; i < prefixSums.length; i++) {
prefixSums[i] = (i == 0) ? array[i] : array[i] + prefixSums[i - 1];
}
for (int to = 0; to < prefixSums.length; to++) {
if (prefixSums[to] == sum)
return true; // interval [0 .. to]
for (int from = 0; from < to; from++) {
if (prefixSums[to] - prefixSums[from] == sum)
return true; // interval (from .. to]
}
}
return false;
}
BTW I think reading the input values from Scanner inside the static initializer is a bad idea, why don't you move them to main()?

count islands of zeros in a matrix

I am trying to program following well-known counting islands problem.
and it is not giving me the expected output. Where am I going wrong?
My assumption is if 0's touch 0th row or column or dimension of matrix .. it will not be treated as island
Here is my code
public class Matrix {
static int rowCount = 5;
static int columnCount = 4;
static int[][] matrix = { {1,1,1,1,1},
{1,0,0,0,1},
{1,1,1,1,1},
{1,1,1,0,1}
};
static boolean[][] visited = new boolean[rowCount][columnCount];
private static int countIslands = 0;
public static void main(String[] args) {
try{
for(int i=0; i<rowCount; i++){
for(int j=0; j<columnCount; j++){
if(matrix[i][j]==0){
checkZeros(matrix, i, j);
System.out.println("returned " + i + j);
}
}
}
System.out.println(visited);
}catch(Exception e){
}
System.out.println(countIslands);
}
private static void checkZeros(int[][] matrix2, int i, int j) {
boolean valueWithinLimits = withinLimits(i,j);
System.out.println("checking for " + i + j);
if(valueWithinLimits) && checkAlreadyVisited(i,j)){
if(matrix[i][j+1]==0){
checkZeros(matrix2, i, j+1);
}
if(matrix[i+1][j+1]==0){
checkZeros(matrix2, i+1, j+1);
}
if(matrix[i+1][j]==0){
checkZeros(matrix2, i+1, j);
}
if(matrix[i+1][j-1]==0){
checkZeros(matrix2, i-1, j-1);
}
visited[i][j] = true;
System.out.println("i reached here when ij are : " + i + j);
countIslands ++;
}
}
private static boolean checkAlreadyVisited(int i, int j) {
System.out.println("visited found for " + i + j);
return visited[i][j-1] || visited[i-1][j-1] || visited[i-1][j] || visited[i-1][j+1];
}
private static boolean withinLimits(int i, int j) {
return (i>0 && i<rowCount-1 && j>0 && j<columnCount-1);
}
}
The below solution is tested and works perfectly fine for any possibility
package com.divyanshu.island;
/**
* <b>Assumption 1 : 1 is Land, 0 is water.</b>
* <b>Assumption 2 : It is all water outside the matrix.</b>
*
* Instantiate IslandCounter by passing a m*n matrix.
* Method getIslandCount gives you the count of island formed.
*
* </br></br>Or</br></br>
*
* Method getIslandCount gives the count of all connected 1s in a m*n matrix with values in 1 or 0.
*/
public class IslandCounter {
private Integer[][] matrix;
public IslandCounter(Integer[][] matrix) {
this.matrix = matrix;
}
public int getIslandCount() {
int count = 0;
if (matrix == null || matrix.length == 0) {
return count;
}
Integer[][] tempMatrix = matrix.clone();
for (int i = 0; i < tempMatrix.length; i++) {
for (int j = 0; j < tempMatrix[i].length; j++) {
if (detectIsland(tempMatrix, false, i, j, matrix.length - 1, matrix[i].length - 1)) {
count++;
}
}
}
return count;
}
private boolean detectIsland(Integer[][] tempMatrix,
boolean islandDetected,
int i,
int j,
int iMax,
int jMax) {
if (i > iMax || j > jMax || i < 0 || j < 0 || tempMatrix[i][j] == 0) {
return islandDetected;
} else {
tempMatrix[i][j] = 0;
islandDetected = true;
detectIsland(tempMatrix, islandDetected, i - 1, j, iMax, jMax);
detectIsland(tempMatrix, islandDetected, i, j - 1, iMax, jMax);
detectIsland(tempMatrix, islandDetected, i + 1, j, iMax, jMax);
detectIsland(tempMatrix, islandDetected, i, j + 1, iMax, jMax);
}
return islandDetected;
}
}
===================================================================================
/**
*
*/
package com.divyanshu.island;
import java.util.Random;
/**
*This is a Main-Class to test the IslandCounter.
*/
public class IslandTest {
/**
* #param args
*/
public static void main(String[] args) {
Integer[][] matrix = generateMatrix();
printMatrix(matrix);
IslandCounter counter = new IslandCounter(matrix);
System.out.println("Total islands in the matrix : " + counter.getIslandCount());
}
private static Integer[][] generateMatrix() {
Integer[][] matrix = new Integer[4][4];
Random random = new Random();
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
matrix[i][j] = random.nextInt(2);
}
}
return matrix;
}
private static void printMatrix(Integer[][] matrix) {
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
System.out.print(matrix[i][j] + " ");
}
System.out.println();
}
}
}
What you can do to improve is to add an exclusion array: an array of elements that are zeros. For example, if you find a zero within limits, you can start looking around and see if there are any zeros. Keep looking until you find all of them. Then add every single one of these zeros to the exclusion array, and when you continue with the loop, make sure it skips the elements in the exclusion array. This is not code, but the outline of the logic of the program.
I think there are multiple problems in your code.
Your visited matrix is full of false, which mean that checkAlreadyVisited will always return false. Also, I don't understand why does this method checks surroundings to see if the current location is visited. Using a temporary matrix like visited is a good idea, but you should print both map to ensure that it works.
countIslands is never incremented because of the previous error, but once you'll have resolved it, it will be incremented on every call (which should match the number of 0 on your map). If you want his solution to work with the border constraint, you must apply his detectIsland on each border before the for loop.
Divyanshu's solution works, except that it counts 1 and doesn't consider that an island touching a border is not an island (as you said).
To correct your solution, the visited matrix must be a copy of the matrix before using it, checkAlreadyVisited should only scan [i][j] and not its surrondings, and you shouldn't increment countIslands at each call.
Again, print your maps at each turns and use an easier matric like:
static int[][] matrix = {{1,1,1},
{1,0,1},
{1,1,1},};
(Didn't saw this question was three monsth old... anyway, here you go)

Any faster solution other than this bruteforce approach

I've been submitting programs to this problem at ACM. Problem ID=1922 but my solution keeps getting Time Limit Exceeded on test 3.
My idea is to use brute-force but with some branches-cutting-off. The below is my Java code, any faster solutions or improvements would be appreciated... I guess this isn't difficult at all because the difficulty is only 195, but I just can't get it accepted.
Finally got it accepted. The algorithm is to first sort the heroes, and start with the smallest-wish first. Just O(n)..
My Java code is so far the fastest Solution Rank
Many thanks!
public class testtest
{
static boolean[] used;
// length of heros
static int ulen;
// list of heros
static Wish[] w;
// number of possible teams
static int count = 0;
// and output
static StringBuilder str = new StringBuilder();
// add the team
// check if it is a valid team
static boolean check(int len)
{
for (int i = 0; i < ulen; i ++)
{
if (!used[i])
{
// adding another hero makes it reliable, so invalid
if (w[i].wish <= len + 1)
{
return false;
}
}
}
return true;
}
// search the teams, team size = total, current pick = len, start from root + 1
static void search(int root, int total, int len)
{
if (len >= total) // finish picking len heros
{
if (check(total)) // valid
{
print(total); // add to output
}
return;
}
for (int i = root + 1; i < ulen; i ++)
{
if (w[i].wish > len + ulen - i)
{
return; // no enough heros left, so return
}
else
if (w[i].wish <= total) // valid hero for this team
{
used[i] = true;
search(i, total, len + 1); // search next hero
used[i] = false;
}
}
}
public static void main(String[] args) throws IOException
{
BufferedReader rr = new BufferedReader(new InputStreamReader(System.in));
ulen = Integer.parseInt(rr.readLine());
w = new Wish[ulen];
for (int i = 0; i < ulen; i ++)
{
w[i] = new Wish(i + 1, Integer.parseInt(rr.readLine()));
}
Arrays.sort(w);
used = new boolean[ulen];
Arrays.fill(used, false);
for (int i = 1; i <= ulen; i ++)
{
for (int j = 0; j <= ulen - i; j ++)
{
if (w[j].wish <= i) // this hero is valid
{
used[j] = true;
search(j, i, 1);
used[j] = false;
}
}
}
System.out.println(count);
System.out.print(str);
}
}
First, my results (of Java) is the fastest.
http://acm.timus.ru/rating.aspx?space=1&num=1922&lang=java
The fact that I didn't make full use before is that I have sorted list of heroes according to their wishes.
Therefore, the main loop just needs to be changed to O(n) instead of O(n^2)
for (int i = 1; i <= ulen; i ++)
{
if (w[0].wish <= i)
{
used[0] = true;
search(0, i, 1);
used[0] = false;
}
}
Here is what I have that executes for the sample test in ~0.00013 seconds (on my CPU):
import java.io.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Arrays;
/**
* Hero.java
*
* This program solves the Super Hero problem put forth by Timus Online Judge
* http://acm.timus.ru/problem.aspx?space=1&num=1922
*
* #author Hunter McMillen
* #version 1.0 12/29/2012
*/
public class Hero {
private static Map<Integer, Integer> indexMap = new HashMap<Integer, Integer>();
private static List<Integer> indices = new ArrayList<Integer>();
private static boolean[] used;
/**
* Entry point into the application
*
* #args command line arguments
*/
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
int numHeroes, wish;
List<Integer> heroes = new ArrayList<Integer>();
List<List<Integer>> groups;
// read number of heroes
numHeroes = Integer.parseInt(in.readLine());
// read 'numHeroes' wishes from stdin
// filter out heroes that have a minimum required that exceeds
// the number of heroes
for(int i = 0; i < numHeroes; i++) {
wish = Integer.parseInt(in.readLine());
if(wish <= numHeroes)
heroes.add(wish);
}
// split into groups
groups = reliableGroups(heroes);
// output results
System.out.println(groups.size());
for(List<Integer> g : groups) {
System.out.println(g.size() + " " + g.toString().replaceAll("[\\]\\[\\,]", ""));
}
}
/**
* Determines whether a group is effective, meaning that all wishes
* for that group are met
*
* #group The group to evaluate for effectiveness
*/
public static boolean isEffective(List<Integer> group) {
int maxWish = Integer.MIN_VALUE;
int temp;
// find the maximum wish size of all members in group
for(int i = 0; i < group.size(); i++) {
if((temp = indexMap.get(group.get(i))) > maxWish)
maxWish = temp;
}
// make sure that the maximum wish size is respected
return group.size() >= maxWish;
}
/**
* Checks to see if there exists some other superhero
* that when added to this group makes another effective group
*
* #effectiveGroup The current grouping that is effective but might
* not be reliable
*/
public static boolean isReliable(List<Integer> effectiveGroup) {
for(int i = 1; i <= indices.size(); i++) {
if(!used[i]) {
// add another hero to this group to see if it remains effective
effectiveGroup.add(i);
// if it is still effective, then this group is not reliable
if(isEffective(effectiveGroup))
return false;
// remove the hero that was temporarily added
effectiveGroup.remove(effectiveGroup.size()-1);
}
}
// true if adding any unused member to this group made it ineffective
return true;
}
/**
* Separates the List<Integer> of heroes into reliable groups
*
* #heroes The List of heroes
*/
public static List<List<Integer>> reliableGroups(List<Integer> heroes) {
List<List<Integer>> groups = new ArrayList<List<Integer>>();
boolean effective = true;
int h, current;
// create HashMap with mapping between hero wish values and their index
for(int i = 1; i <= heroes.size(); i++) {
indices.add(i);
indexMap.put(i, heroes.get(i-1));
}
// create an array to track which heroes have been used
used = new boolean[indices.size()+1];
Arrays.fill(used, false);
List<int[]> combinations;
List<Integer> tempList;
for(int i = 1; i <= indices.size(); i++) {
h = indexMap.get(i);
combinations = combination(heroes, h);
// iterate over all combinations making sure the wish values are below
// the threshold for this hero at map index `i`
for(int[] aCombination : combinations) {
for(int j = 0; j < aCombination.length; j++) {
current = aCombination[j];
used[current] = true;
if(indexMap.get(current) > h) {
effective = false;
break;
}
}
// create a List from the integer[] combination
tempList = asList(aCombination);
// if the group makeup is reliable, save it
if(effective && !groups.contains(tempList) && isReliable(tempList))
groups.add(new ArrayList<Integer>(tempList));
// reset flags
effective = true;
Arrays.fill(used, false);
}
}
return groups;
}
/**
* Helper method that returns a List<Integer> given
* an array of primitive ints
*
* #array The array to convert to a List<Integer>
*/
public static List<Integer> asList(int[] array) {
List<Integer> boxed = new ArrayList<Integer>();
for(int i = 0; i < array.length; i++) {
boxed.add(array[i]);
}
return boxed;
}
/**
* Generates the intial r combination in ascending order
* i.e [1, 2, 3, 4, ..., r]
*
* #r The size of the intial combination
*/
public static int[] initialCombination(int r) {
int[] indices = new int[r];
for(int i = 0; i < r; i++)
indices[i] = i+1;
return indices;
}
/**
* Generates the next combination given an array of indices
*
* #indicesIn The array of indices
* #n The size of this combination
*/
public static int[] nextCombination(int[] indicesIn, int n) {
int[] indices = (int[])indicesIn.clone();
int r = indices.length;
// find the rightmost index that is not at its final highest value
int i = 0;
for (i = r - 1; i >= 0; i--) {
if (indices[i] != (i + n - r + 1)) {
break;
}
}
// return null if no more combinations exist
if (i == -1)
return null;
// increment rightmost index
indices[i]++;
// reset all the indices to the right of indices[i]
// to their smallest possible value.
for (int j = i + 1; j < r; j++) {
indices[j] = indices[j-1] + 1;
}
return indices;
}
/**
* Generates all r-combinations of the indices array
*
* #heroes The array of heroes wishes
* #r The length of the combination to generate
*/
public static List<int[]> combination(List<Integer> heroes, int r) {
List<int[]> combinations = new ArrayList<int[]>();
int[] indices = initialCombination(r);
while(indices != null) {
combinations.add(indices);
indices = nextCombination(indices, heroes.size());
}
return combinations;
}
}

Categories