import SimpleOutput;

/**
 * A very simple computation of the minimum number of stamps 
 * needed to total a particular price.  Based on Stamps.java,
 * given in HW3 of CSC152 2000S.  This extended version reports
 * on the number of function calls and uses a table to improve
 * the running time.
 *
 * @author Samuel A. Rebelsky
 * @version 1.0 of March 2000
 */
public class NewerStamps 
{
  // +--------+--------------------------------------------------
  // | Fields |
  // +--------+

  /** The number of recursive calls executed. */
  protected int numCalls;
  
  /** 
   * An array holding all the stamp values knows.  Has -1 in
   * each cell as a default.
   */
  protected int[] minStamps;
  
  // +---------+-------------------------------------------------
  // | Helpers |
  // +---------+

  /**
   * Compute the minimum number of stamps that exactly
   * totals val cents.  Puts the values of the stamps in
   * stampsToBuy, starting at index stampNum.
   * Pre: (1) val >= 0
   *      (2) All stamps values are positive.
   *      (3) It is possible to combine stamps for any value.
   *      (4) It requires no more than MAXSTAMPS stamps.
   * Post: Returns N such that it is possible to buy N stamps
   *   for exactly val and it is not possible to buy M < N 
   *   stamps for exactly val.
   */
  public int minimumStamps(int val, int[] stampValues)
  {
    // Increment the number of calls, since we're keeping track
    ++numCalls;
    // Preparation: Make sure that minStamps is defined.  This should
    // only be done once overall.
    if ((this.minStamps == null) || (this.minStamps.length <= val)) {
      this.minStamps = new int[val+1];
      for (int i = 1; i <= val; i++)
        minStamps[i] = -1;
      minStamps[0] = 0;
    }
    // Special case: minStamps[val] is not yet set.
    if (minStamps[val] < 0) {
      int i = 0; // An index into stampValues
      int guess; // Our best guess so far as to the minimum.
      int nextGuess;  // Another guess to try
      int buyThisTime; // The stamp to buy in this round.
  
      // Find the first stamp that's still worth buying.
      while (stampValues[i] > val)
        i++;
  
      // We might buy that stamp.
      buyThisTime = stampValues[i];
      guess = 1 + minimumStamps(val-stampValues[i], stampValues);
  
      // But we might also buy other stamps.
      for (i = i+1; i < stampValues.length; i++) {
        if (stampValues[i] <= val) {
          nextGuess =
            1 + minimumStamps(val-stampValues[i], stampValues);
          if (nextGuess < guess) {
            buyThisTime = stampValues[i];
            guess = nextGuess;
          } // if it's a better guess
        } // if the stamp is worth buying.
      } // for
      
      // Okay, we have a final solution
      minStamps[val] = guess;
    } // Special case
    
    // Okay, the array is now filled in!
    return minStamps[val];
  } // minimumStamps(int, int[])

  // +------+----------------------------------------------------
  // | Main |
  // +------+

  public static void main(String[] args) {
    int val = 0;
    SimpleOutput out = new SimpleOutput();   
    NewerStamps helper = new NewerStamps();
    // Set up the array of stamp values.
    int[] stamps = { 1, 5, 20, 33, 50 };

    // Sanity check.
    if (args.length != 1) {
      out.println("Usage: java Stamps value");
      System.exit(1);
    }
    // Get the value.
    try { val = (new Integer(args[0])).intValue(); }
    catch (NumberFormatException e) {
      out.println("The value must be an integer.");
      System.exit(2);
    } // catch
    // Verify that the preconditions are met.
    if (val < 0) {
      out.println("The value must be positive.");
      System.exit(3);
    } // if (val < 0)
    // Compute away!
    helper.numCalls = 0;
    int numStamps = helper.minimumStamps(val, stamps);
    if (numStamps == 0)
      out.println("You need no stamps to make no cents.");
    else if (numStamps == 1)
      out.println("You need one stamp to make " + val + " cents.");
    else
      out.println("You need " + numStamps + " stamps to make " 
                  + val + " cents.");
    out.println("That used " + helper.numCalls + " recursive calls.");
  } // main(String[])

} // class NewerStamps
