#include #include #include #include #include #define MAX_SIZE 50 struct Need { void reset() { _need = 0; } bool needed(int type) { return (_need & (1 << type)) == 0; } void done(int type) { _need |= (1 << type); } private: int _need; }; struct Piece { int size; int min; int max; bool pos[MAX_SIZE]; void clear(int size) { min = size; max = 0; } bool empty() { return min >= max; } void init(int p, int s) { min = p; max = p + s; for (int i = 0; i < MAX_SIZE; i++) pos[i] = min <= i && i < max; } void assign(const Piece& p) { size = p.size; min = p.min; max = p.max; for (int i = 0; i < MAX_SIZE; i++) pos[i] = p.pos[i]; } void update(const Piece& p, bool& changed) { min = p.min; max = p.max; for (int i = 0; i < MAX_SIZE; i++) if (pos[i] && !p.pos[i]) { pos[i] = false; changed = true; } } bool reset(int i) { if (pos[i]) { pos[i] = false; if (i == min) { min++; while (min < max && !pos[min]) min++; } else if (i == max-1) { max--; while (min < max && !pos[max-1]) max--; } } return min < max; } bool limit_min(int i) { while (min < i && min < max) reset(min); return min < max; } bool limit_max(int i) { while (i < max && min < max) reset(max-1); return min < max; } bool set(int i) { pos[i] = true; if (i < min) min = i; if (i+1 > max) max = i+1; } }; struct Pieces { int nr; Piece pieces[MAX_SIZE]; Need need; void add(int v) { pieces[nr++].size = v; } void init(int size) { need.reset(); int sum = 0; for (int i = 0; i < nr; i++) sum += pieces[i].size; int left = size - (sum + nr - 1); if (left < 0) left = 0; int p = 0; for (int i = 0; i < nr; i++) { pieces[i].init(p, left+1); p += pieces[i].size + 1; } } void assign(const Pieces& ps) { need.reset(); nr = ps.nr; for (int i = 0; i < nr; i++) pieces[i].assign(ps.pieces[i]); } void update(const Pieces& ps, bool changed) { for (int i = 0; i < nr; i++) pieces[i].update(ps.pieces[i], changed); } }; int n; int m; int nm; struct Clues { Pieces row[MAX_SIZE]; Pieces col[MAX_SIZE]; void assign(const Clues& clues) { for (int i = 0; i < n; i++) row[i].assign(clues.row[i]); for (int i = 0; i < m; i++) col[i].assign(clues.col[i]); } }; #define UNKNOWN '?' #define FULL '*' #define EMPTY '_' struct Board { Need rowneeds[MAX_SIZE]; Need colneeds[MAX_SIZE]; int nr_unknown; char operator[](const int i) const { return _b[i]; } void clear() { nm = n * m; for (int i = 0; i < nm; i++) _b[i] = UNKNOWN; nr_unknown = nm; } void copy(const Board& board) { for (int i = 0; i < nm; i++) _b[i] = board._b[i]; nr_unknown = board.nr_unknown; resetneeds(); } void clone(const Board& board) { for (int i = 0; i < nm; i++) if (board._b[i] == UNKNOWN) _b[i] = ' '; else _b[i] = board._b[i]; nr_unknown = 0; resetneeds(); } void assign(const int i, const int v) { if (v == UNKNOWN) { if (_b[i] != UNKNOWN) nr_unknown++; } else { if (_b[i] == UNKNOWN) nr_unknown--; } _b[i] = v; } void combine(const int i, const int v) { if (_b[i] == ' ') { _b[i] = v; if (v == UNKNOWN) nr_unknown++; } else if (_b[i] != v && _b[i] != UNKNOWN) { _b[i] = UNKNOWN; nr_unknown++; } } void combine(const Board& board) { for (int i = 0; i < nm; i++) combine(i, board[i]); } void intergrate(const int i, const int val, int &modified) { if (_b[i] == UNKNOWN && (val == FULL || val == EMPTY)) { modified++; _b[i] = val; rowneeds[i%n].reset(); colneeds[i/n].reset(); nr_unknown--; } } void intergrate(const Board& board, int &modified) { for (int i = 0; i < nm; i++) intergrate(i, board[i], modified); } void resetneeds() { for (int i = 0; i < MAX_SIZE; i++) { rowneeds[i].reset(); colneeds[i].reset(); } } int count_unknown() { int result = 0; for (int i = 0; i < nm; i++) if (_b[i] == UNKNOWN) result++; return result; } private: char _b[MAX_SIZE*MAX_SIZE]; }; void read_line(FILE* f, char *line, bool &more) { if (fgets(line, 299, f) == 0) { more = false; return; } if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = '\0'; more = true; } bool read_clues(char *fn, Clues &clues) { FILE* f = fopen(fn, "rt"); char line[300]; bool more = true; read_line(f, line, more); while (more && strncmp(line, "width ", 6) != 0) read_line(f, line, more); if (!more) { fclose(f); return false; } m = atoi(line+6); read_line(f, line, more); while (more && strncmp(line, "height ", 7) != 0) read_line(f, line, more); if (!more) { fclose(f); return false; } n = atoi(line+7); read_line(f, line, more); while (more && strcmp(line, "rows") != 0) read_line(f, line, more); if (!more) { fclose(f); return false; } for (int i = 0; i < n; i++) { read_line(f, line, more); if (!more) { fclose(f); return false; } clues.row[i].nr = 0; int sum = 0; if (strcmp(line, "0") != 0) { char *s = line; while (*s != '\0') { int v = 0; for (; isdigit(*s); s++) v = 10 * v + *s - '0'; for(; *s == ',' || *s == ' '; s++) ; clues.row[i].add(v); } } clues.row[i].init(m); } read_line(f, line, more); while (more && strcmp(line, "columns") != 0) read_line(f, line, more); if (!more) { fclose(f); return false; } for (int i = 0; i < m; i++) { read_line(f, line, more); if (!more) { fclose(f); return false; } clues.col[i].nr = 0; int k = 0; if (strcmp(line, "0") != 0) { char *s = line; while (*s != '\0') { int v = 0; for (; isdigit(*s); s++) v = 10 * v + *s - '0'; for(; *s == ',' || *s == ' '; s++) ; clues.col[i].add(v); } } clues.col[i].init(n); } nm = n * m; fclose(f); return true; } void printsolution(Board &board) { for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) printf("%c", board[i+n*j]); printf("\n"); } } bool reason(char *line, int size, Pieces &ps) { for (bool go = true; go; ) { go = false; bool notempty[MAX_SIZE]; // true if there is a piece placement covering cell for (int i = 0; i < size; i++) notempty[i] = false; // Based on the state of "line" determine which piece placements // are possible. for (int i = 0; i < ps.nr; i++) { Piece& p = ps.pieces[i]; int empty_from = 0; if (i > 0) empty_from = ps.pieces[i-1].max + ps.pieces[i-1].size; int empty_to = size; if (i+1 < ps.nr) empty_to = ps.pieces[i+1].min; for (int j = p.min; j < p.max; j++) if (p.pos[j]) { int e = j + p.size; // A placement of piece i at postion j is only possible // if the location before and after it are not full // and the locations inside are not empty bool possible = (j == 0 || line[j-1] != FULL) && (e == size || line[e] != FULL); for (int k = empty_from; k < j && possible; k++) if (line[k] == FULL) possible = false; for (int k = j; k < e && possible; k++) if (line[k] == EMPTY) possible = false; for (int k = e; k < empty_to && possible; k++) if (line[k] == FULL) possible = false; if (possible) { for (int k = j; k < e; k++) notempty[k] = true; } else { go = true; if (!p.reset(j)) { // No placement left //printf("x1\n"); return false; } } } } for (int i = 1; i < ps.nr; i++) { int min = ps.pieces[i-1].min + ps.pieces[i-1].size + 1; Piece& p = ps.pieces[i]; if (min > p.min) { if (!p.limit_min(min)) { //printf("x2\n"); return false; } go = true; } } for (int i = ps.nr - 2; i >= 0; i--) { int max = ps.pieces[i+1].max - ps.pieces[i].size - 1; Piece& p = ps.pieces[i]; if (max < p.max) { if (!p.limit_max(max)) { //printf("piece %d: limit max %d: %d-%d\n", i, max, p.min, p.max); //printf("x3\n"); return false; } go = true; } } // Pieces that are limited by their movement // result in cells that are filled. for (int i = 0; i < ps.nr; i++) { Piece& p = ps.pieces[i]; bool pp = false; for (int k = p.max - 1; k < p.min + p.size; k++) { if (line[k] == EMPTY) { //printf("x4\n"); return false; } if (line[k] != FULL) { line[k] = FULL; go = true; } } } // Set cells that are not covered by any piece placement for (int i = 0; i < size; i++) if (!notempty[i]) { if (line[i] == FULL) { //printf("x5\n"); return false; } if (line[i] != EMPTY) { line[i] = EMPTY; go = true; } } } return true; } bool reason(char *rowcol, int nr, Board &board, int pos, int off, int size, Pieces& pieces, int& modified) { char line[MAX_SIZE]; for (int i = 0; i < size; i++) line[i] = board[pos + off*i]; Pieces ps; ps.assign(pieces); if (!reason(line, size, ps)) return false; for (int i = 0; i < size; i++) board.intergrate(pos + off*i, line[i], modified); bool changed = false; pieces.update(ps, changed); if (changed) { modified++; } return true; } bool solve(Clues &clues, Board &board) { fprintf(stderr, "solve\n"); int l = 0; int modified; do { modified = 0; for (int i = 0; i < n; i++) if (board.rowneeds[i].needed(1) && !reason("rij", i+1, board, i, n, m, clues.row[i], modified)) return false; else board.rowneeds[i].done(1); for (int j = 0; j < m; j++) if (board.colneeds[j].needed(1) && !reason("colom", j+1, board, j*n, 1, n, clues.col[j], modified)) return false; else board.colneeds[j].done(1); } while (modified > 0); return true; } int main(int argc, char *argv[]) { Clues clues; read_clues(__FILE__, clues); Board board; board.clear(); if (!solve(clues, board)) printf("\nNo solution\n"); else printsolution(board); } /* width 34 height 34 rows 1 1 2 2 3 3 2 1 1 2 2 1 1 2 1 1 1 1 1 1 1 1 18 2 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 26 2 1 1 1 1 1 1 2 2 1 1 2 2 1 1 2 2 1 1 2 2 1 1 2 14 14 1 1 1 1 1 1 1 1 14 14 2 1 1 2 2 1 1 2 2 1 1 2 2 1 1 2 2 1 1 1 1 1 1 2 26 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 2 18 1 1 1 1 1 1 1 1 2 1 1 2 2 1 1 2 3 3 2 2 1 1 columns 1 1 2 2 3 3 2 1 1 2 2 1 1 2 1 1 1 1 1 1 1 1 18 2 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 26 2 1 1 1 1 1 1 2 2 1 1 2 2 1 1 2 2 1 1 2 2 1 1 2 14 14 1 1 1 1 1 1 1 1 14 14 2 1 1 2 2 1 1 2 2 1 1 2 2 1 1 2 2 1 1 1 1 1 1 2 26 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 2 18 1 1 1 1 1 1 1 1 2 1 1 2 2 1 1 2 3 3 2 2 1 1 */