-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtwofive.cpp
173 lines (156 loc) · 4.81 KB
/
twofive.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/*
ID: paulius10
PROG: twofive
LANG: C++
*/
#include <algorithm>
#include <fstream>
#include <map>
#include <set>
#include <vector>
using namespace std;
const int EDGE = 5;
const int SIZE = EDGE * EDGE;
const char EMPTY = '_';
const char FIRST_LETTER = 'A';
const char LAST_LETTER = FIRST_LETTER + SIZE - 1;
// the number of ways to complete a partially filled grid with this signature
map<vector<int>, int> completions;
/* Checks if grid[index] is a legal assignment. */
bool possible(int index, vector<char> &grid) {
// all cells to the left must be less than grid[index]
for (int i = index - index % EDGE; i < index; ++i)
if (grid[i] >= grid[index])
return false;
// same for cells above
for (int i = index - EDGE; i >= 0; i -= EDGE)
if (grid[i] >= grid[index])
return false;
// ensure uniqueness
for (int i = 0; i < index; i++)
if (grid[i] == grid[index])
return false;
return true;
}
/* A partially filled grid which doesn't skip any letters can be represented by
a vector of EDGE numbers in [0, EDGE] based on the number of characters in
each row. Permutations don't matter (as long as they are legal), each row is
filled from left to right, and each row has at most as many letters as the
row above. */
vector<int> signature(vector<char> &grid) {
vector<int> t(EDGE, 0);
for (int i = 0; i < SIZE; ++i)
if (grid[i] != EMPTY)
++t[i / 5];
return t;
}
/* Recursively generates a vector of all possible signatures. */
void generate_signatures(vector<int> signature,
vector<vector<int> > &all_signatures) {
int size = signature.size();
if (size == EDGE) {
all_signatures.push_back(signature);
return;
}
for (int i = 0; i <= signature[size - 1]; ++i) {
vector<int> next_signature = signature;
next_signature.push_back(i);
generate_signatures(next_signature, all_signatures);
}
}
/* A comparator for signature vectors based on their sums. */
int compare_sums(vector<int> a, vector<int> b) {
int s1 = 0, s2 = 0;
for (int i = 0; i < EDGE; i++) {
s1 += a[i];
s2 += b[i];
}
return s1 > s2;
}
/* Counts the number ways to complete a partially filled grid using memoized
recursion and the completions map. */
map<vector<char>, int> memo;
int count_completions(vector<char> grid) {
// memoization
map<vector<char>, int>::iterator it = memo.find(grid);
if (it != memo.end())
return it->second;
// find the smallest letter that's not in the grid
set<char> letters;
for (int i = 0; i < SIZE; ++i)
if (grid[i] != EMPTY)
letters.insert(grid[i]);
char letter = FIRST_LETTER;
for (; letters.find(letter) != letters.end() && letter <= LAST_LETTER;
++letter);
// if we didn't skip any letters, we can use completions[]
if (letter >= *(letters.rbegin())) {
memo[grid] = completions[signature(grid)];
return memo[grid];
}
// recurse on all possible spots to place this letter
int sum = 0;
for (int i = 0; i < SIZE; ++i) {
if (grid[i] == EMPTY) {
grid[i] = letter;
if (possible(i, grid))
sum += count_completions(grid);
grid[i] = EMPTY;
}
}
return memo[grid] = sum;
}
int main() {
// generate all possible signatures and sort them (necessary for DP)
vector<vector<int> > signatures;
for (int i = 0; i <= EDGE; ++i)
generate_signatures(vector<int>(1, i), signatures);
sort(signatures.begin(), signatures.end(), compare_sums);
// generate the completions map
completions[signatures[0]] = 1;
for (int i = 1; i < signatures.size(); ++i) {
completions[signatures[i]] = 0;
for (int j = 0; j < EDGE; ++j) {
if (signatures[i][j] < EDGE && (j == 0 || signatures[i][j] <
signatures[i][j - 1])) {
// since each successor has a bigger sum, its value is already computed
vector<int> successor = signatures[i];
++successor[j];
completions[signatures[i]] += completions[successor];
}
}
}
int skipped = 0; // the number of possible grids that we're skipping
vector<char> grid(SIZE, EMPTY);
char mode;
ifstream fin("twofive.in");
fin >> mode;
ofstream fout("twofive.out");
if (mode == 'N') {
int M;
fin >> M;
for (int i = 0; i < SIZE; ++i) {
for (grid[i] = FIRST_LETTER; grid[i] <= LAST_LETTER; ++grid[i]) {
if (possible(i, grid)) {
int num_ways = count_completions(grid);
if (skipped + num_ways >= M)
break;
skipped += num_ways;
}
}
}
for (int i = 0; i < 25; ++i)
fout << grid[i];
} else {
string target;
fin >> target;
for (int i = 0; i < SIZE; ++i)
for (grid[i] = 'A'; grid[i] < target[i]; ++grid[i])
if (possible(i, grid))
skipped += count_completions(grid);
fout << skipped + 1;
}
fin.close();
fout << endl;
fout.close();
}