/* Import the Java I/O package */
import java.io.*;
import AppletConsoleApp;

/**
 * Solution to the 1999 British Informatics Olympiad exam
 * question 3: Playing Games
 *
 * This application that can be run either from the console
 * as a stand-alone application or from an in-applet
 * console created with the AppletConsole applet.
 *
 * Solution copyright (c) 1999 The British Informatics Olympiad (BIO).
 *
 * This program may be freely copied by persons or organisations
 * involved in the British Informatics Olympiad or the International
 * Olympiad in Informatics, on condition that no changes are made and
 * this notice is not altered. Distribution for profit is forbidden
 * unless permission is first obtained in writing from the BIO.
 *
 * This program is for educational purposes only and comes with no
 * warranty, implied or otherwise, as to its fitness for any purpose.
 *
 * Author:     Antony Rix
 * Internet:   http://www.christs.cam.ac.uk/bio/
 * E-mail:     a.rix@lineone.net
 * S-mail:     The British Informatics Olympiad
 *             Christ's College
 *             Cambridge CB2 3BU
 *             United Kingdom
 *
 * @author    Antony Rix
 * @version 0.1
 * @see         AppletConsoleApp
 * @see         AppletConsole
 */
class BIO99R1Q3App extends AppletConsoleApp {
    /**
     * Start the application from the command line.
     */
    public static void main(String[] args) {
        BIO99R1Q3App thisApp = new BIO99R1Q3App();
        thisApp.redirectStreams(System.in, System.out);
        thisApp.run();
    }

    /**
     * A StreamTokenizer object allow us to easily read in the input.
     */
    StreamTokenizer sin;

    /**
     * The implementation of this application.
     */
    public void run() {
        int opt;
        try {
            /* StreamTokenizer to read input */
            sin = new StreamTokenizer( in );

            do {
                out.println(
                    "BIO'99 question 3. Enter one of the following options." );
                out.println(
                    "1 Simple (and often incorrect) greedy solution to part 3a" );
                out.println(
                    "2 Test the dynamic programming method" );
                out.println(
                    "3 Full, correct dynamic programming solution to part 3a" );
                out.println(
                    "4 Solution to part 3c" );
                out.println(
                    "\n0 Exit" );
                out.print( ">" );
                sin.nextToken();
                opt = (int)sin.nval;
                switch (opt) {
                    case 1: if( read_input() ) { solve_greedy(); } break;
                    case 2: test_dynamic(); break;
                    case 3: if( read_input() ) { solve_dynamic(); } break;
                    case 4: part_3c(); break;
                }
                out.print( "\n" );
            } while (opt != 0);
        } catch (IOException e) { out.println("I/O failure"); };
        out.println( "Program finished." );
    }

    /**
     * Input data.
     */
    int n;
    int[] scores = new int[10];
    int m;
    int[] totals = new int[10];

    /**
     * Arrays used for the dynamic programming solution.
     */
    int[] num_rounds = new int[1001];
    int[] last_score = new int[1001];

    /**
     * Get the input for part 3a.
     */
    public boolean read_input() {
        int i;
        try {
            out.print( "Value of n:" );
            sin.nextToken();
            n = (int)sin.nval;
            if( (n < 1) || (n > 10) ) {
                out.println( "n must be from 1 to 10!" );
                return false;
            }
            out.print( "Enter " + n + " different positive scores separated by space.\n:" );
            for( i = 0; i < n; i++ ) {
                sin.nextToken();
                scores[i] = (int)sin.nval;
                if( (scores[i] < 1) || (scores[i] > 1000) ) {
                    out.println( "Scores must be from 1 to 1000!" );
                    return false;
                }
            }
            out.print( "Value of m:" );
            sin.nextToken();
            m = (int)sin.nval;
            if( (m < 1) || (m > 10) ) {
                out.println( "m must be from 1 to 10!" );
                return false;
            }
            out.print( "Enter " + m + " totals separated by space.\n:" );
            for( i = 0; i < m; i++ ) {
                sin.nextToken();
                totals[i] = (int)sin.nval;
            }
        } catch (IOException e) { out.println("I/O failure"); return false; };
        return true;
    }

    /**
     * Simple, greedy solution to the problem.  This is often incorrect.
     */
    public void solve_greedy() {
        int i, j, s, r;
        int[] num = new int[10];

        /* Sort the scores into decreasing order using a bubble sort */
        for( i = 1; i < n; i++ ) for( j = 1; j < n; j++ )
            if( scores[j] > scores[j-1] ) {
                s = scores[j]; scores[j] = scores[j-1]; scores[j-1] = s;
            }
        /* Test the sort using the following statement */
        /* for( i = 0; i < n; i++ ) out.println( scores[i] ); */

        /* For each total, allocate the scores in a greedy manner */
        for( i = 0; i < m; i++ ) {
            for( j = 0; j < n; j++ ) num[j] = 0;
            s = totals[i];
            j = 0;
            r = 0;
            while( (s != 0) && (j < n) ) {
                /* Test if score[j] is possible.  If so, choose it, otherwise
                   move on to the next score. */
                if( s >= scores[j] ) {
                    s = s - scores[j];
                    num[j] = num[j] + 1;
                    r = r + 1;
                }
                else
                    j = j + 1;
            }
            /* Show result for this total in increasing order of score if
               it has been possible to get the right total, or print Impossible */
            if( s == 0 ) {
                out.print( "Total " + totals[i] + " in " + r + " rounds:" );
                for( j = n-1; j >= 0; j-- ) if( num[j] > 0 )
                    out.print( " " + num[j] + "x" + scores[j] );
                out.print( "\n" );
            }
            else
                out.print( "Total " + totals[i] + " is Impossible\n" );
        }
    }

    /**
     * Finds the possible scores and (for testing) displays them if required.
     * Uses a dynamic programming method.
     */
    public void find_dynamic( boolean show ) {
        int i, j;

        /* Initialise the arrays that store the number of rounds required to
           reach each value, and the last score. */
        for( i = 1; i <= 1000; i++) {
            num_rounds[i] = 9999;
            last_score[i] = 9999;
        }
        num_rounds[0] = 0;
        last_score[0] = 0;

        /* Find the scores required to reach each total */
        i = 0;
        while( i <= 1000 ) {
            /* For each possible score, see if the new totals are reached
               in fewer rounds than before. */
            for( j = 0; j < n; j++ ) if( i + scores[j] <= 1000 )
                if( num_rounds[i + scores[j]] > num_rounds[i] + 1 ) {
                    num_rounds[i + scores[j]] = num_rounds[i] + 1;
                    last_score[i + scores[j]] = scores[j];
                }

            /* Show the steps leading to this point */
            if( (i > 0) && show ) {
                out.print( i + ":" );
                j = i;
                while( j > 0 ) {
                    out.print( last_score[j] + " " );
                    j = j - last_score[j];
                }
                out.print( "\n" );
            }

            /* Find the next valid total */
            while( true ) {
                i++;
                if( i > 1000 ) break; /* We are off the end of the array */
                if( num_rounds[i] < 9999 ) break; /* We have a valid total */
            }
        }
    }

    /**
     * Illustrates the dynamic method.
     */
    public void test_dynamic() {
        int i;

        /* Read in the scores */
        try {
            out.print( "Value of n:" );
            sin.nextToken();
            n = (int)sin.nval;
            if( (n < 1) || (n > 10) ) {
                out.println( "n must be from 1 to 10!" );
                return;
            }
            out.print( "Enter " + n + " different positive scores separated by space.\n:" );
            for( i = 0; i < n; i++ ) {
                sin.nextToken();
                scores[i] = (int)sin.nval;
                if( (scores[i] < 1) || (scores[i] > 1000) ) {
                    out.println( "Scores must be from 1 to 1000!" );
                    return;
                }
            }
        } catch (IOException e) { out.println("I/O failure"); return; };

        /* Use find_dynamic to find and show the results */
        find_dynamic( true );
    }

    /**
     * Full dynamic programming solution to 3a using find_dynamic.
     */
    public void solve_dynamic() {
        int i, j, p, c;

        /* Use find_dynamic to find the possible totals */
        find_dynamic( false );

        /* For each required total, test if it is reachable */
        for( i = 0; i < m; i++ ) {
            if( (totals[i] < 1) || (totals[i] > 1000) )
                out.print( "Total " + totals[i] + " is Impossible - out of range" );
            else if( num_rounds[totals[i]] == 9999 )
                out.print( "Total " + totals[i] + " is Impossible" );
            else {
                out.print( "Total " + totals[i] + " in " + num_rounds[totals[i]] + " rounds:" );
                /* Find the number of times each score is required. */
                for( p = 0; p < n; p++ ) {
                    c = 0;
                    j = totals[i];
                    while( j > 0 ) {
                        if( last_score[j] == scores[p] ) c++;
                        j = j - last_score[j];
                    }
                    if( c > 0 )
                        out.print( " " + c + "x" + scores[p] );
                }
            }
            out.print( "\n" );
        }
    }

    /**
     * Solution to part 3c.
     * Remus is playing a game where it is possible to score
     * 1, 4, 5, 17, 28, 43 or 100 each round. At the end of the game the final
     * score is 100. Furthermore, the scores for each round never got worse,
     * e.g. if 17 was scored in one round then the score for every future
     * round was at least 17. How many different ways might this have happened?
     */
    int[] scores3c = { 1, 4, 5, 17, 28, 43, 100 };
    public void part_3c() {
        /* To solve this we use a slightly different method.  As there is
           a score of 1 any score between 1 and 100 is possible; the problem
           is to count the number of permutations that reach each total.
           We do this using the recursive procedure perms_3c(). */
        out.println( "Number of different ways: " + perms_3c( 100, 6 ) );
    }
    public int perms_3c( int total, int num ) {
        /* Returns the number of permutations of the first num scores that
           sum to total, in strictly increasing order.  Calls itself. */
        int perms = 0;
        int current = total;

        /* There is only one way to reach a total of zero. */
        if( total == 0 ) return 1;

        /* There is only one way to reach any total using only 1s. */
        if( num == 0 ) return 1;

        /* Otherwise we count the permutations that finish with zero or
           more instances of the last number. */
        while( current >= 0 ) {
            perms = perms + perms_3c( current, num-1 );
            current = current - scores3c[num];
        }
        return perms;
    }
}
