263 lines
9.0 KiB
Java
Executable File
263 lines
9.0 KiB
Java
Executable File
// 2003 ACM Mid-Central Regional Programming Contest
|
|
// Problem G: Fax Regions
|
|
// by Andy Harrington, Loyola University Chicago
|
|
/*
|
|
fax.java
|
|
Algorithm that shrinks the fax to one with sides O(runs) and calculate
|
|
components conventionally then
|
|
|
|
Problem: given fax width and run lengths, calculate the number of
|
|
connected components.
|
|
|
|
1. Process runs initially
|
|
a. dropping full rows of same
|
|
b. if 1+2k full rows of different, drop the first 2k of these rows,
|
|
remembering the previous row and k
|
|
2. Look at the increasing sequence of x coordinates where shifts between same
|
|
and different take place, and remap these x coordinates onto
|
|
0,1,2,....newWidth, maintaining the order.
|
|
3. Do the conversion to a grid that is at most 1*runs across by 4*runs.
|
|
4. Calculate connected components by a conventional recursive routine
|
|
5. In each case where 2k rows in a run of differences was removed, add
|
|
k * (number of sideways transitions between black and white across the
|
|
previous row)
|
|
to the number of components, to get the total.
|
|
|
|
The program prints reduced grids to the screen if they are small enough.
|
|
|
|
You may also test the direct algorithm on small datasets by running the
|
|
program with a commandline parameter. This displays the full grid
|
|
solutions and writes the output to a file smallFax.out as long as the
|
|
widths and heights are no more than 80.
|
|
*/
|
|
|
|
|
|
import java.io.*;
|
|
import java.util.*;
|
|
import java.awt.Point;
|
|
|
|
class fax {
|
|
|
|
static final int MAXWIDTH = 1000000000;
|
|
static final int END_INPUT = -1;
|
|
static final int MAX_RUNS = 1000;
|
|
static final int MAX_DATASETS = 25; // for testing
|
|
static final char EMPTY = '.';
|
|
static final char UNVISITED = '*';
|
|
static final String compLabel = // for screen display
|
|
"123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
|
|
static char[][] fillGrid(int h, int w, Point[] changeCoord, int runs) {
|
|
// fill grid of h rows and w columns so first and last rows are empty
|
|
// given the coordinates where sequences of sames and differents end
|
|
int x, y;
|
|
char grid[][] = new char[h][w];
|
|
for (x = 0; x < w; x++)
|
|
grid[h-1][x] = grid[0][x] = EMPTY; // extra first and last row empty
|
|
boolean matches = true;
|
|
int i = 0;
|
|
for (y = 1; y < h-1; y++)
|
|
for (x = 0; x < w; x++) {
|
|
if (changeCoord[i].x == x && changeCoord[i].y == y) {
|
|
matches = !matches;
|
|
i++;
|
|
}
|
|
grid[y][x] = matches ?
|
|
grid[y-1][x] : (char)(EMPTY + UNVISITED - grid[y-1][x]);
|
|
}
|
|
return grid;
|
|
}
|
|
|
|
static int countComps(char[][] grid) {
|
|
// return the numer of components in a grid of EMPTY and UNVISITED
|
|
int h = grid.length - 1;
|
|
int w = grid[0].length;
|
|
int x, y;
|
|
int comps = 0;
|
|
for (y = 1; y < h; y++)
|
|
for (x = 0; x < w; x++) {
|
|
if (grid[y][x] == UNVISITED) {
|
|
fillComp(grid, y, x, compLabel.charAt(comps % compLabel.length()));
|
|
comps++;
|
|
}
|
|
}
|
|
return comps;
|
|
}
|
|
|
|
static void fillComp(char[][] grid, int y, int x, char fillChar) {
|
|
// recursively fill the unvisited component including (x, y) with fillChar
|
|
grid[y][x] = fillChar;
|
|
if (grid[y-1][x] == UNVISITED) fillComp(grid, y-1, x, fillChar);
|
|
if (grid[y+1][x] == UNVISITED) fillComp(grid, y+1, x, fillChar);
|
|
if (x > 0 && grid[y][x-1] == UNVISITED) fillComp(grid, y, x-1, fillChar);
|
|
if (x < grid[0].length-1 && grid[y][x+1] == UNVISITED)
|
|
fillComp(grid, y, x+1, fillChar);
|
|
}
|
|
|
|
static PrintWriter makeOutput(String name) {
|
|
PrintWriter out = null;
|
|
try {
|
|
out = new PrintWriter(
|
|
new BufferedWriter(
|
|
new FileWriter(name)));
|
|
} catch(Exception e) {
|
|
System.out.println("can't open output");
|
|
}
|
|
return out;
|
|
}
|
|
|
|
public static void main(String[] arg) {
|
|
String FILE = "fax";
|
|
ACMIO in = new ACMIO(FILE + ".in");
|
|
PrintWriter out = makeOutput(FILE+".out");
|
|
|
|
if (arg.length > 0) {
|
|
smallFax();
|
|
return;
|
|
}
|
|
Point[] changeCoord = new Point[MAX_RUNS];
|
|
int datasets = 0;
|
|
|
|
int w = in.intRead();
|
|
while ( w != END_INPUT) { // one dataset per loop
|
|
datasets++;
|
|
int y = 1; // border at row 0
|
|
int x = 0;
|
|
int totRuns = in.intRead();
|
|
if (totRuns > MAX_RUNS) System.out.println("Error -- runs: " + totRuns +
|
|
" > " + MAX_RUNS + " :max runs");
|
|
in.intRead(); // ignore runs per line -- put in for Pascal
|
|
Vector delPairs = new Vector();
|
|
|
|
for (int runs = 0; runs < totRuns; runs++) {
|
|
int n = in.intRead();
|
|
if (runs % 2 == 0) { // run of same
|
|
if (n + x >= w) // never add more than one line
|
|
y++;
|
|
}
|
|
else { // run of different
|
|
int dy = (n+x)/w;
|
|
if (dy > 3) { // guarantees all different for 3 full rows
|
|
int killPairs = (dy/2) - 1;
|
|
delPairs.add(new int[] {y, killPairs});
|
|
dy -= 2*killPairs;
|
|
}
|
|
y += dy;
|
|
}
|
|
x = (x + n) % w;
|
|
changeCoord[runs] = new Point(x, y);
|
|
}
|
|
|
|
if (x > 0) System.out.println("Error -- ending x is not 0 but " + x);
|
|
|
|
// shift x coordinates so all x coordinates 0,1, ... w-1 (with new w)
|
|
Point[] sorted = (Point[])changeCoord.clone(); // shallow clone important
|
|
Arrays.sort(sorted, 0, totRuns,
|
|
new Comparator() {
|
|
public int compare(Object p1, Object p2) {
|
|
return ((Point)p1).x - ((Point)p2).x;
|
|
}
|
|
public boolean equals(Object p) {
|
|
return compare(this, p) == 0;
|
|
}
|
|
}
|
|
);
|
|
// testing line
|
|
System.out.println("Dataset " + datasets + ": Orig width : " + w);
|
|
|
|
int newX = 0, oldX = 0;
|
|
for (int i = 0; i < totRuns; i++) {
|
|
if (sorted[i].x > oldX) {
|
|
newX++;
|
|
oldX = sorted[i].x;
|
|
}
|
|
sorted[i].x = newX; // also changes element of changeCoord
|
|
}
|
|
w = newX + 1;
|
|
|
|
char[][] grid = fillGrid(y+1, w, changeCoord, totRuns);
|
|
|
|
int comps = countComps(grid);
|
|
//testing line
|
|
System.out.print("Reduced grid: " +
|
|
(y-1) + " X " + w + " with " + comps + " components");
|
|
for (int i = delPairs.size() - 1; i >= 0; i--) {
|
|
int[] pair = (int[])(delPairs.get(i));
|
|
y = pair[0];
|
|
int changes = 0;
|
|
for (x = 1; x < w; x++)
|
|
if (grid[y][x-1] != grid[y][x])
|
|
changes++;
|
|
comps += (changes+1)*pair[1];
|
|
}
|
|
System.out.println(", final total: " + comps); // testing line
|
|
printGrid(grid);
|
|
|
|
out.println(comps);
|
|
w = in.intRead();
|
|
} // end of dataset
|
|
if (datasets > MAX_DATASETS) System.out.println("Error -- datasets: " +
|
|
datasets + " > " + MAX_DATASETS + " : max runs"); //testing line
|
|
out.close();
|
|
}
|
|
|
|
// The rest of the class is only used for testing //////////////////////////
|
|
|
|
static void printGrid(char[][] g) {
|
|
System.out.println();
|
|
if (g.length > 80 || g[0].length > 80)
|
|
System.out.println("Skipping big grid");
|
|
else
|
|
for (int r = 1; r < g.length - 1; r++)
|
|
System.out.println(g[r]);
|
|
System.out.println();
|
|
}
|
|
|
|
// The last method smallFax is just to test small faxes with the obvious
|
|
// algorithm: it is activated if fax is run with a commandline parameter.
|
|
|
|
static void smallFax() {
|
|
ACMIO in = new ACMIO("fax.in");
|
|
PrintWriter out = makeOutput("smallFax.out");
|
|
int datasets = 0;
|
|
|
|
int w = in.intRead(); // next dataset
|
|
while ( w <= 80) { // stop at big dataset
|
|
datasets++;
|
|
int totRuns = in.intRead();
|
|
in.intRead(); // ignore runs per line -- put in for Pascal
|
|
char[][] grid = new char[82][w]; // assume will be small
|
|
for (int i = 0; i < w; i++)
|
|
grid[0][i] = EMPTY;
|
|
int y = 1; // border at row 0
|
|
int x = 0;
|
|
boolean matches = true;
|
|
for (int runs = 0; runs < totRuns; runs++) {
|
|
int n = in.intRead();
|
|
for ( ; n > 0 ; n--) {
|
|
grid[y][x] = matches ?
|
|
grid[y-1][x] : (char)(EMPTY + UNVISITED - grid[y-1][x]);
|
|
if (x == w-1) {
|
|
x = 0;
|
|
y++;
|
|
}
|
|
else x++;
|
|
}
|
|
matches = !matches;
|
|
}
|
|
if (x > 0) System.out.println("Error -- ending x is not 0 but " + x);
|
|
char[][] fullGrid = new char[y+1][];
|
|
System.arraycopy(grid, 0, fullGrid, 0, y);
|
|
fullGrid[y] = fullGrid[0];
|
|
int comps = countComps(fullGrid);
|
|
|
|
System.out.println("Small Dataset "+datasets+ ": " +comps+ " components");
|
|
printGrid(fullGrid);
|
|
|
|
out.println(comps);
|
|
w = in.intRead();
|
|
} // end of dataset
|
|
out.close(); // omits lines starting with first input width > 80
|
|
} // end smallFax testing alternative
|
|
}
|