#include #include #include #pragma GCC diagnostic ignored "-Wsign-compare" typedef unsigned int uint; typedef uint64_t u64; #define B(x) ((u64)1 << (x)) int edges[52][53]; u64 neigh[52]; uint n; uint cost[52]; int v2i(char c) { if (c >= 'A' && c <= 'Z') return c - 'A'; if (c >= 'a' && c <= 'z') return c - 'a' + 26; assert(0); } char i2v(char i) { return (i < 26) ? i + 'A' : i - 26 + 'a'; } void read_input(void) { unsigned char buf[256]; int j = 0; while (scanf("%s", buf) == 1) { assert(buf[0] == i2v(n)); assert(buf[1] == ':'); for (j=0; buf[2+j]; j++) { int w = v2i(buf[2+j]); assert(w != n); edges[n][j] = w; neigh[n] |= B(w); } edges[n][j] = -1; n++; } } u64 cover=0; uint cover_size = 100; #if 0 void find_cover(void) { // Toto je evidentně správně uint cover_size = 100; for (u64 c=0; c < B(n); c++) { uint w = 0; uint size = 0; for (uint i=0; i < n; i++) { if (c & B(i)) { size++; w |= neigh[i] | (1U << i); } } if (w == B(n) - 1 && size < cover_size) { cover = c; cover_size = size; } } } #elif 0 void find_cover(void) { // Enumerace množin od nejmenší k největší for (uint size=1; size < n && !cover; size++) { uint c = B(size) - 1; while (c < B(n)) { // printf("%08x\n", c); uint w = 0; for (uint i=0; i < n; i++) if (c & B(i)) w |= neigh[i] | B(i); if (w == B(n) - 1) { cover = c; break; } int p, q; for (p=0; !(c & B(p)); p++) ; for (q=p; c & B(q); q++) ; c = c & ~(B(q) - 1); c |= B(q); if (q > p + 1) c |= B(q-p-1) - 1; } } } #else // Backtrack s ořezáváním void bt_cover(uint start, uint nchosen, u64 chosen, u64 remains) { // printf(">> %016lx %016lx %d\n", remains, chosen, nchosen); for (uint i=start; i < n; i++) { if (!(chosen & B(i))) { uint nch = nchosen + 1; u64 ch = chosen | B(i); u64 rm = remains & ~(B(i) | neigh[i]); if (!rm) { if (nch < cover_size) { cover = ch; cover_size = nch; return; } } else if (rm != remains) { bt_cover(i+1, nch, ch, rm); if (nch >= cover_size) return; } } } } void find_cover(void) { bt_cover(0, 0, 0, B(n) - 1); } #endif void print_cover(void) { assert(cover); for (uint i=0; i < n; i++) if (cover & B(i)) putchar(i2v(i)); putchar('\n'); } void find_costs(void) { for (uint i=0; i < n; i++) if (cover & B(i)) { cost[i] = 1000000; for (int j=0; edges[i][j] >= 0; j++) cost[edges[i][j]]++; } #if 0 for (uint i=0; i < n; i++) printf("cost[%d] = %d\n", i, cost[i]); #endif } #define MAXC (52*52) u64 buckets[MAXC]; uint dist[52], pred[52]; void find_path(void) { u64 closed = 0; dist[0] = cost[0]; buckets[dist[0]] = 1; for (uint i=1; i < n; i++) dist[i] = MAXC; for (uint d=0; d < MAXC; d++) { uint i; while ((i = __builtin_ffsll(buckets[d] & ~closed)) > 0) { i--; // printf("%d (%c) at distance %d, pred %d\n", i, i2v(i), dist[i], pred[i]); assert(dist[i] == d); if (i == n-1) return; for (int j=0; edges[i][j] >= 0; j++) { uint w = edges[i][j]; // printf("\t%d (%d) -> %d (%d) new %d\n", i, d, w, dist[w], d + cost[w]); if (dist[w] > d + cost[w]) { dist[w] = d + cost[w]; pred[w] = i; buckets[dist[w]] |= B(w); } } closed |= B(i); } } assert(0); } void print_path(void) { printf("%d\n", dist[n-1]); uint path[52]; uint p = 0; path[p++] = n - 1; while (path[p-1]) { path[p] = pred[path[p-1]]; p++; assert(p <= n); // printf("> %d\n", path[p-1]); } assert(p >= 2); printf("%c\n", i2v(path[p-2])); for (int q=p-3; q >= 0; q--) { uint from = path[q+2], curr = path[q+1], next = path[q]; int i, fpos = -1, npos = -1; for (i=0; edges[curr][i] >= 0; i++) { if (edges[curr][i] == from) fpos = i; if (edges[curr][i] == next) npos = i; } assert(fpos >= 0 && npos >= 0); int d = (npos - fpos + i) % i; if (d > i/2) d -= i; while (d < 0) { putchar('-'); d++; } while (d > 0) { putchar('+'); d--; } putchar('\n'); } } int main(void) { read_input(); find_cover(); print_cover(); find_costs(); find_path(); print_path(); return 0; }