Cod sursa(job #2451771)

Utilizator wumpJustin Fairchild wump Data 28 august 2019 05:48:30
Problema Jocul Flip Scor 0
Compilator java Status done
Runda Arhiva de probleme Marime 9.15 kb
import java.io.File;
import java.io.FileWriter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Iterator;
import java.util.Scanner;

/*
 * Given a game board defined by N and M on the first line (rows and columns),
 * take all the integers in that board and sum them up. Then, given each row/col
 * has a flag for multiplying the row/col by -1, find the maximum sum the board
 * can have.
 */

public class Main {

   public final Integer min_rows = 1;
   public final Integer max_columns = 16;
   public final Integer value_limit = (int) Math.pow(10, 6);

   private Integer n;
   private Integer m;
   private Integer[][] t;
   private enum Direction { ROW, COLUMN };
   private String in_filename;
   private String out_filename;

   // Make a static turn object, so I can make recursive methods not around
   // the table itself, but the stack of turns that transforms the table.
   private class Turn {
      private Direction direction;
      private Integer address;

      Turn(Integer a, Direction d) {
         this.direction = d;
         this.address = a;
      }

      @Override
      public boolean equals(Object o) {
         // This has to override the basic equals method for Objects
         if (o == this) {
            return true;
         }
         if (!(o instanceof Turn)) {
            return false;
         }
         Turn t = (Turn) o;
         return ((this.address.equals(t.address)) &&
                 (this.direction.equals(t.direction)));
      }

      public Integer getAddress() {
         return this.address;
      }

      public Direction getDirection() {
         return this.direction;
      }
   }

   private ArrayDeque<Turn> turn_list = new ArrayDeque<Turn>();
   private ArrayDeque<Turn> history = new ArrayDeque<Turn>();

   // Add a turn to a stack
   public void addTurn(ArrayDeque<Turn> turns, Turn turn) {
      turns.addLast(turn);
   }

   // Empty out a turns list
   public void emptyTurns(ArrayDeque<Turn> turns) {
      turns.forEach(item -> turns.remove());
   }

   public ArrayDeque<Turn> getTurns() {
      return this.turn_list;
   }

   // Does the deque contain the given turn?
   public boolean hasTurn(ArrayDeque<Turn> turns, Turn test) {
      for (Turn move: turns) {
         if(move.equals(test)) {
            return true;
         }
      }
      return false;
   }

   private void readBoard() throws FileNotFoundException, IOException {
      Scanner in = new Scanner(new File(this.in_filename));
      this.n = in.nextInt();
      if (this.n < min_rows) {
         System.exit(1);
      }
      this.m = in.nextInt();
      if (this.m > max_columns) {
         System.exit(1);
      }
      this.t = new Integer[m][n];
      for(int ni = 0; ni < n; ni++) {
         for(int mi = 0; mi < m; mi++) {
            this.t[mi][ni] = in.nextInt();
            if (this.t[mi][ni] > value_limit) {
               System.exit(1);
            }
         }
      }
      in.close();
   }

   // Remove a turn from a stack
   public void removeTurn(ArrayDeque<Turn> turns, Turn turn) {
      turns.removeLastOccurrence(turn);
   }

   private void setInputFile(String in_file) {
      this.in_filename = in_file;
   }

   private void setOutputFile(String out_file) {
      this.out_filename = out_file;
   }

   public void setTurns(ArrayDeque<Turn> list) {
      this.turn_list = list;
   }

   private ArrayDeque<Turn> strategy() throws FileNotFoundException, IOException {
      return strategy(this.turn_list);
   }

   private ArrayDeque<Turn> strategy(ArrayDeque<Turn> my_list) throws FileNotFoundException, IOException {
      Integer max = sumBoard(my_list);   // Current max is board sum
      // If two possible turns are equal, need to do lookahead
      ArrayDeque<Turn> choices = new ArrayDeque<Turn>();
      for(int ni = 0; ni < this.n; ni++) {
         Turn row = new Turn(ni, Direction.ROW);
         Integer sum_current = sumRange(row, false, false);
         Integer sum_test = sumRange(row, true, false);
         // We haven't made this move before.
         if (!hasTurn(this.history, row)) {
            // Move results in a better board sum
            if (sum_test > sum_current) {
               max = sumBoard(my_list, row);
               emptyTurns(choices);
               addTurn(choices, row);
            }
            // Move isn't worse off, so eval later
            if (sum_test == sum_current) {
               addTurn(choices, row);
            }
         }
      }
      for(int mi = 0; mi < this.m; mi++) {
         Turn column = new Turn(mi, Direction.COLUMN);
         Integer sum_current = sumRange(column, false, false);
         Integer sum_test = sumRange(column, true, false);
         // We haven't made this move before.
         if (!hasTurn(this.history, column)) {
            // Move results in a better board sum
            if (sum_test > sum_current) {
               max = sumBoard(my_list, column);
               emptyTurns(choices);
               addTurn(choices, column);
            }
            if (sum_test == sum_current) {
               addTurn(choices, column);
            }   
         }
      }
      // What to do with the choices we have?
      if (choices.size() == 0) {
         // Can't do better. Return the existing turn_list
         return my_list;
      } 
      else if (choices.size() == 1) {
         // One possible move forward
         Turn chosen = choices.remove();
         addTurn(my_list, chosen);
         addTurn(this.history, chosen);
         return strategy(my_list);
      } 
      else {
         // Compare the existing choices, and whichever one has 
         // the better score, return that one.
         // Shallow-clone the turn list
         ArrayDeque<Turn> test_list = my_list.clone();
         ArrayDeque<Turn> best_list = null;
         for(Turn option: choices) {
            addTurn(test_list, option);
            addTurn(this.history, option);
            test_list = strategy(test_list);
            Integer board_test = sumBoard(test_list);
            if (board_test > max) {
               max = board_test;
               best_list = test_list.clone();
            } else {
               // Keep a valid choice for another branch
               // and reset the state of the search list
               removeTurn(this.history, option);
               test_list = my_list.clone();
            }
         }
         return (best_list != null) ? best_list : my_list;
      }
   }
   
   // Default to use the object's turn stack
   private Integer sumBoard() throws FileNotFoundException, IOException {
      return sumBoard(this.turn_list);
   }

   // Calculate new board sums based off of a turn we plan to take
   private Integer sumBoard(ArrayDeque<Turn> my_list, Turn extra) throws FileNotFoundException, IOException {
      ArrayDeque<Turn> temp = my_list.clone();
      addTurn(temp, extra);
      return sumBoard(temp);
   }

   // Do a board sum on any given turn stack you want to test
   private Integer sumBoard(ArrayDeque<Turn> my_list) throws FileNotFoundException, IOException {
      Integer sum = 0;
      // If we have a list of turns or flips, persist them
      for(Turn my_turn: my_list) {
         sumRange(my_turn, true, true);   // Persist turn-based board changes
      }      
      // Now calculate the board
      for(int ni = 0; ni < this.n; ni++) {
         Turn row = new Turn(ni, Direction.ROW);
         sum += sumRange(row, false, false);
      }
      // And reset the board back to normal, in lieu of deepClone games
      // for Java 2D arrays, which I don't want to deal with
      readBoard();
      return sum;
   }

   /*
    * Given a starting position and a direction, calculate either the sum
    * of a range, or the inverted (flipped) sum of that range. When running
    * through a set of inversion turns, you can optionally persist the changes
    * into the table, so that other turns will see that state.
    */
   private Integer sumRange(Turn range, boolean flip, boolean persist) {
      Integer mult = flip ? -1 : 1;
      Integer sum = 0;
      boolean write = (flip && persist);   // No need for writeback if flip not enabled
      Direction d = range.getDirection();
      Integer a = range.getAddress();
      if (d.equals(Direction.ROW)) {
         for(int mi = 0; mi < this.m; mi++) {
            sum += this.t[mi][a] * mult;
            if (write) this.t[mi][a] *= mult;   // Persist in the table if necessary
         }
      } else if (d.equals(Direction.COLUMN)) {
         for(int ni = 0; ni < n; ni++) {
            sum += this.t[a][ni] * mult;
            if (write) this.t[a][ni] *= mult;   // Persist in the table if necessary
         }
      } else {
         System.exit(1);
      }
      return sum;
   }

   private void writeResult(String output) throws FileNotFoundException, IOException {
      FileWriter out = new FileWriter(new File(this.out_filename));
      out.append(output);
      out.close();
   }

   public static void main(String[] args) throws FileNotFoundException, IOException {
      Main game = new Main();
      game.setInputFile("flip.in");
      game.setOutputFile("flip.out");
      game.readBoard();
      game.setTurns(game.strategy());
      game.writeResult(game.sumBoard().toString());
      System.exit(0);
   }
}