Problem A. Tic-Tac-Toe-Tomek
Problem
Tic-Tac-Toe-Tomek is a game played on a 4 x 4 square board. The board starts empty, except that a single 'T' symbol may appear in one of the 16 squares. There are two players: X and O. They take turns to make moves, with X starting. In each move a player puts her symbol in one of the empty squares. Player X's symbol is 'X', and player O's symbol is 'O'.
After a player's move, if there is a row, column or a diagonal containing 4 of that player's symbols, or containing 3 of her symbols and the 'T' symbol, she wins and the game ends. Otherwise the game continues with the other player's move. If all of the fields are filled with symbols and nobody won, the game ends in a draw. See the sample input for examples of various winning positions.
Given a 4 x 4 board description containing 'X', 'O', 'T' and '.' characters (where '.' represents an empty square), describing the current state of a game, determine the status of the Tic-Tac-Toe-Tomek game going on. The statuses to choose from are:
- "X won" (the game is over, and X won)
- "O won" (the game is over, and O won)
- "Draw" (the game is over, and it ended in a draw)
- "Game has not completed" (the game is not over yet)
Input
The first line of the input gives the number of test cases, T.T test cases follow. Each test case consists of 4 lines with 4 characters each, with each character being 'X', 'O', '.' or 'T' (quotes for clarity only). Each test case is followed by an empty line.
Output
For each test case, output one line containing "Case #x: y", where x is the case number (starting from 1) and y is one of the statuses given above. Make sure to get the statuses exactly right. When you run your code on the sample input, it should create the sample output exactly, including the "Case #1: ", the capital letter "O" rather than the number "0", and so on.
Limits
The game board provided will represent a valid state that was reached through play of the game Tic-Tac-Toe-Tomek as described above.
Small dataset
1 ≤ T ≤ 10.
Large dataset
1 ≤ T ≤ 1000.
Sample
Input | Output |
6 | Case #1: X won |
Note
Although your browser might not render an empty line after the last test case in the sample input, in a real input file there would be one.
#include <cstdio>
#include <cstdlib>
inline bool equal(const char ch, const char symbol) {
return (ch == symbol || ch == 'T');
}
bool check(const char board[4][5], const char symbol) {
for (int i = 0; i < 4; ++i) {
int cntrow = 0, cntcol = 0;
for (int j = 0; j < 4; ++j) {
if (equal(board[i][j], symbol))
++cntrow;
if (equal(board[j][i], symbol))
++cntcol;
}
if (cntrow == 4 || cntcol == 4)
return true;
}
int cntdiag1 = 0, cntdiag2 = 0;
for (int i = 0, j = 0, k = 3; i < 4; ++i, ++j, --k) {
if (equal(board[i][j], symbol))
++cntdiag1;
if (equal(board[i][k], symbol))
++cntdiag2;
}
if (cntdiag1 == 4 || cntdiag2 == 4)
return true;
return false;
}
int main() {
FILE *fin, *fout;
fopen_s(&fin, "A-large.in", "r");
fopen_s(&fout, "A-large.out", "w");
int T;
fscanf_s(fin, "%d", &T);
for (int casenum = 1; casenum <= T; ++casenum) {
fprintf(fout, "Case #%d: ", casenum);
char board[4][5];
for (int i = 0; i < 4; ++i)
fscanf_s(fin, "%s", board[i], _countof(board[i]));
if (check(board, 'X')) {
fprintf(fout, "X won\n");
} else if (check(board, 'O')) {
fprintf(fout, "O won\n");
} else {
bool filled = true;
for (int i = 0; i < 4 && filled; ++i)
for (int j = 0; j < 4 && filled; ++j)
if (board[i][j] == '.')
filled = false;
if (filled)
fprintf(fout, "Draw\n");
else
fprintf(fout, "Game has not completed\n");
}
}
system("pause");
return 0;
}
Problem B. Lawnmower
Problem
Alice and Bob have a lawn in front of their house, shaped like an N metre byM metre rectangle. Each year, they try to cut the lawn in some interesting pattern. They used to do their cutting with shears, which was very time-consuming; but now they have a new automatic lawnmower with multiple settings, and they want to try it out.
The new lawnmower has a height setting - you can set it to any height h between 1 and 100 millimetres, and it will cut all the grass higher thanh it encounters to height h. You run it by entering the lawn at any part of the edge of the lawn; then the lawnmower goes in a straight line, perpendicular to the edge of the lawn it entered, cutting grass in a swath 1m wide, until it exits the lawn on the other side. The lawnmower's height can be set only when it is not on the lawn.
Alice and Bob have a number of various patterns of grass that they could have on their lawn. For each of those, they want to know whether it's possible to cut the grass into this pattern with their new lawnmower. Each pattern is described by specifying the height of the grass on each 1m x 1m square of the lawn.
The grass is initially 100mm high on the whole lawn.
Input
The first line of the input gives the number of test cases, T.T test cases follow. Each test case begins with a line containing two integers:N and M. Next follow N lines, with theith line containing M integers ai,j each, the numberai,j describing the desired height of the grass in thejth square of the ith row.
Output
For each test case, output one line containing "Case #x: y", where x is the case number (starting from 1) and y is either the word "YES" if it's possible to get the x-th pattern using the lawnmower, or "NO", if it's impossible (quotes for clarity only).
Limits
1 ≤ T ≤ 100.
Small dataset
1 ≤ N, M ≤ 10.
1 ≤ ai,j ≤ 2.
Large dataset
1 ≤ N, M ≤ 100.
1 ≤ ai,j ≤ 100.
Sample
Input | Output |
3 | Case #1: YES |
Analysis: For each cell, check if its value is larger or equal than any other values in the same row / column. If both smaller, then the grass height is impossible.
#include <cstdio>
#include <algorithm>
int main() {
FILE *fin, *fout;
fopen_s(&fin, "B-large.in", "r");
fopen_s(&fout, "B-large.out", "w");
int T;
fscanf_s(fin, "%d", &T);
for (int casenum = 1; casenum <= T; ++casenum) {
fprintf(fout, "Case #%d: ", casenum);
int a[100][100];
int n, m;
fscanf_s(fin, "%d%d", &n, &m);
int rowmax[100] = {0}, colmax[100] = {0};
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
fscanf_s(fin, "%d", &a[i][j]);
rowmax[i] = std::max(rowmax[i], a[i][j]);
colmax[j] = std::max(colmax[j], a[i][j]);
}
}
bool ans = true;
for (int i = 0; i < n && ans; ++i)
for (int j = 0; j < m && ans; ++j)
if (a[i][j] < rowmax[i] && a[i][j] < colmax[j])
ans = false;
if (ans)
fputs("YES\n", fout);
else
fputs("NO\n", fout);
}
return 0;
}
Problem C. Fair and Square
Problem
Little John likes palindromes, and thinks them to be fair (which is a fancy word for nice). Apalindrome is just an integer that reads the same backwards and forwards - so 6, 11 and 121 are all palindromes, while 10, 12, 223 and 2244 are not (even though 010=10, we don't consider leading zeroes when determining whether a number is a palindrome).
He recently became interested in squares as well, and formed the definition of afair and square number - it is a number that is a palindrome and thesquare of a palindrome at the same time. For instance, 1, 9 and 121 are fair and square (being palindromes and squares, respectively, of 1, 3 and 11), while 16, 22 and 676 arenot fair and square: 16 is not a palindrome, 22 is not a square, and while 676 is a palindrome and a square number, it is the square of 26, which is not a palindrome.
Now he wants to search for bigger fair and square numbers. Your task is, given an interval Little John is searching through, to tell him how many fair and square numbers are there in the interval, so he knows when he has found them all.
Solving this problem
Usually, Google Code Jam problems have 1 Small input and 1 Large input. This problem has 1 Small input and 2 Large inputs. Once you have solved the Small input, you will be able to download any of the two Large inputs. As usual, you will be able to retry the Small input (with a time penalty), while you will get only one chance at each of the Large inputs.
Input
The first line of the input gives the number of test cases, T.T lines follow. Each line contains two integers, A andB - the endpoints of the interval Little John is looking at.
Output
For each test case, output one line containing "Case #x: y", where x is the case number (starting from 1) and y is the number of fair and square numbers greater than or equal toA and smaller than or equal to B.
Limits
Small dataset
1 ≤ T ≤ 100.
1 ≤ A ≤ B ≤ 1000.
First large dataset
1 ≤ T ≤ 10000.
1 ≤ A ≤ B ≤ 1014.
Second large dataset
1 ≤ T ≤ 1000.
1 ≤ A ≤ B ≤ 10100.
Sample
Input | Output |
3 | Case #1: 2 |
Analysis: For the small and first large dataset (64-bit integer), it is possible to precompute and store the required numbers and use binary search to find the answer.
#include <algorithm>
#include <cstdint>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
bool ispalindrome(int64_t n) {
std::stringstream ss;
ss << n;
std::string str = ss.str();
for (int i = 0, j = str.length() - 1; i < j; ++i, --j)
if (str[i] != str[j])
return false;
return true;
}
int main() {
std::vector<int64_t> num;
for (int64_t i = 1; i < 100000000; ++i)
if (ispalindrome(i)) {
int64_t t = i * i;
if (ispalindrome(t))
num.push_back(t);
}
printf("num.size = %d\n", num.size());
for (int i = 0; i < num.size(); ++i)
std::cout << num[i] << '\n';
std::ifstream fin("C-large-1.in");
std::ofstream fout("C-large-1.out");
int T;
fin >> T;
for (int casenum = 1; casenum <= T; ++casenum) {
int64_t a, b;
fin >> a >> b;
int low = std::lower_bound(num.begin(), num.end(), a) - num.begin();
int high = std::upper_bound(num.begin(), num.end(), b) - num.begin();
fout << "Case #" << casenum << ": ";
fout << (high - low) << '\n';
}
fin.close();
fout.close();
system("pause");
return 0;
}
Another python solution, coded by neal.wu, is as below. Seems using some construction method to find palindrome, which may be the solution for second large dataset, but neal.wu had one wrong try for it. Maybe some bug here.
# 3 is a special case
# Otherwise, contains only 1's and 2's. At most one 2
def is_palindrome(string):
i, j = 0, len(string) - 1
while i < j:
if string[i] != string[j]:
return False
i += 1
j -= 1
return True
LEN = 27
def trim(string):
while string and string[0] == '0':
string = string[1:]
return string
def make_palindrome(string):
string = trim(string)
return [string + string[::-1], string[:-1] + string[::-1]]
def candidates():
possible = []
for n in xrange(1, LEN + 1):
for a in xrange(n):
for b in xrange(a, n):
for c in xrange(b, n):
for d in xrange(c, n):
for e in [0, n - 1]:
s = '0' * n
s = s[:a] + '1' + s[a + 1:]
possible += make_palindrome(s)
s = s[:b] + '1' + s[b + 1:]
possible += make_palindrome(s)
s = s[:c] + '1' + s[c + 1:]
possible += make_palindrome(s)
s = s[:d] + '1' + s[d + 1:]
possible += make_palindrome(s)
s = s[:e] + '2' + s[e + 1:]
possible += make_palindrome(s)
s = s[:a] + '0' + s[a + 1:]
possible += make_palindrome(s)
s = s[:b] + '0' + s[b + 1:]
possible += make_palindrome(s)
s = s[:c] + '0' + s[c + 1:]
possible += make_palindrome(s)
s = s[:d] + '0' + s[d + 1:]
possible += make_palindrome(s)
possible.append(3)
possible = list(set(possible))
cands = []
for p in possible:
if p and is_palindrome(str(int(p) * int(p))):
cands.append(int(p) * int(p))
cands.sort()
return cands
cands = candidates()
def lower_bound(list, num):
lo, hi = 0, len(list)
while lo < hi:
mid = (lo + hi) // 2
if list[mid] >= num:
hi = mid
else:
lo = mid + 1
return lo
for test_case in xrange(1, int(raw_input()) + 1):
A, B = map(int, raw_input().split())
total = lower_bound(cands, B + 1) - lower_bound(cands, A)
print "Case #{0}: {1}".format(test_case, total)
The following is accepted code from 'netkuba', the champion of this qualification round.
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
vector< vector<char> > fair;
bool cmp(const vector<char> & a, const vector<char> & b) {
if (a.size() != b.size()) {
return a.size() < b.size();
}
for (int i=0; i<a.size(); ++i) {
if (a[i] != b[i]) {
return a[i] < b[i];
}
}
return true;
}
void fill(int p, int l, vector<char> & result, int rest) {
if (p*2 + 1 == l) {
int i=0;
while(i*i <= rest) {
result[p] = i;
fill(p+1, l, result, rest - i*i);
++i;
}
} else if (p*2 + 1 > l) {
fair.push_back(vector<char>());
for (int i=0; i<result.size(); ++i) {
fair.back().push_back(result[i]);
}
if ((l & 1) == 0) {
fair.back().push_back(result.back());
}
for (int i=result.size()-2; i>=0; --i) {
fair.back().push_back(result[i]);
}
} else {
int i=0;
if (p==0) ++i;
while (i*i*2 <= rest) {
result[p] = i;
fill(p+1, l, result, rest - i*i*2);
++i;
}
}
}
vector<char> square(vector<char> a) {
vector<char> result(a.size() * 2, 0);
for (int i=0; i<a.size(); ++i) {
for (int j=0; j<a.size(); ++j) {
result[i+j] += a[i] * a[j];
}
}
while ((result.size() > 1) && (result.back() == 0)) {
result.pop_back();
}
return result;
}
int find(vector<char> num) {
int A = 0, B = fair.size(), C;
while (B-A > 1) {
C = (A+B)/2;
if (cmp(fair[C], num)) {
A = C;
} else {
B = C;
}
}
return A;
}
void print(vector<char> a) {
for (int i=0; i<a.size(); ++i) {
printf("%d", a[i]);
}
printf("\n");
}
char A[200], B[200];
int counter = 0;
void make() {
printf("Case #%d: ", ++counter);
scanf(" %s %s", A, B);
vector<char> AA, BB;
for (int i=0; A[i] != NULL; ++i) {
AA.push_back(A[i]-'0');
}
int i=AA.size() - 1;
while(AA[i] == 0) {
AA[i] = 9;
--i;
}
--AA[i];
for (int i=0; B[i] != NULL; ++i) {
BB.push_back(B[i]-'0');
}
int a_pos = find(AA);
int b_pos = find(BB);
/*
print(AA);
print(fair[a_pos]);
print(BB);
print(fair[b_pos]);
*/
printf("%d\n", b_pos - a_pos);
}
void preprocessing() {
vector<char> digit(1);
digit[0] = 0;
fair.push_back(digit);
digit[0] = 1;
fair.push_back(digit);
digit[0] = 2;
fair.push_back(digit);
digit[0] = 3;
fair.push_back(digit);
for (int i=2; i<52; ++i) {
vector<char> result((i+1)/2);
fill(0, i, result, 9);
}
/*
digit.resize(9);
digit[0] = 4;
for (int i=1; i<9; ++i) {
digit[i] = 0;
}
int k = 0;
while(cmp(fair[k], digit)) {
for (int i=0; i<fair[k].size(); ++i) {
printf("%d", fair[k][i]);
}
printf("\n");
++k;
}
*/
// printf("%d\n", (int)fair.size());
for (int i=0; i<fair.size(); ++i) {
fair[i] = square(fair[i]);
}
}
int main() {
preprocessing();
int t; scanf("%d", &t);
while(t--) {
make();
}
return 0;
}
Problem D. Treasure
Problem
Following an old map, you have stumbled upon the Dread Pirate Larry's secret treasure trove!
The treasure trove consists of N locked chests, each of which can only be opened by a key of a specific type. Furthermore, once a key is used to open a chest, it can never be used again. Inside every chest, you will of course find lots of treasure, and you might also find one or more keys that you can use to open other chests. A chest may contain multiple keys of the same type, and you may hold any number of keys.
You already have at least one key and your map says what other keys can be found inside the various chests. With all this information, can you figure out how to unlock all the chests?
For example, suppose the treasure trove consists of four chests as described below, and you began with exactly one key of type 1:
Chest Number | Key Type To Open Chest | Key Types Inside --------------+--------------------------+------------------ 1 | 1 | None 2 | 1 | 1, 3 3 | 2 | None 4 | 3 | 2You can open all the chests in this example if you do them in the order 2, 1, 4, 3. If you start by opening chest #1 first, then you will have used up your only key, and you will be stuck.
Input
The first line of the input gives the number of test cases, T.T test cases follow. Each test case begins with a single line containing two positive integersK and N, representing the number of keys you start with and the number of chests you need to open.
This is followed by a line containing K integers, representing the types of the keys that you start with.
After that, there will be N lines, each representing a single chest. Each line will begin with integersTi and Ki, indicating the key type needed to open the chest and the number of keys inside the chest. These two integers will be followed byKi more integers, indicating the types of the keys contained within the chest.
Output
For each test case, output one line containing "Case #x: C1 C2 ... CN", where x is the case number (starting from 1), and where Ci represents the index (starting from 1) of the ith chest that you should open.
If there are multiple ways of opening all the chests, choose the "lexicographically smallest" way. In other words, you should choose to make C1 as small as possible, and if there are multiple ways of making C1 as small as possible, choose the one that makes C2 as small as possible, and so on.
If there is no way to open all the chests, you should instead output one line containing "Case #x: IMPOSSIBLE".
Limits
1 ≤ T ≤ 25.
1 ≤ K.
All key types will be integers between 1 and 200 inclusive.
Small dataset
1 ≤ N ≤ 20.
In each test case, there will be at most 40 keys altogether.
Large dataset
1 ≤ N ≤ 200.
In each test case, there will be at most 400 keys altogether.
Sample
Input | Output |
3 | Case #1: 2 1 4 3 |
The following are accepted codes from neal.wu
For the small dataset:
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <limits>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <utility>
#include <vector>
using namespace std;
const int N_MAX = 22, SUB_MAX = (1 << 20) + 5, KEY_MAX = 42;
int K, N;
vector<int> start;
int open[N_MAX];
vector<int> inside[N_MAX];
int have[KEY_MAX];
bool possible[SUB_MAX];
int first[SUB_MAX];
void initialize()
{
}
void add(vector<int> keys)
{
for (int i = 0; i < (int) keys.size(); i++)
have[keys[i]]++;
}
void solve_case(int test_case)
{
scanf("%d %d", &K, &N);
start.clear();
for (int i = 0; i < N; i++)
inside[i].clear();
for (int i = 0; i < K; i++)
{
int x;
scanf("%d", &x);
start.push_back(x);
}
for (int i = 0; i < N; i++)
{
int n;
scanf("%d %d", &open[i], &n);
for (int j = 0; j < n; j++)
{
int x;
scanf("%d", &x);
inside[i].push_back(x);
}
}
memset(possible, false, sizeof(possible));
memset(first, -1, sizeof(first));
possible[0] = true;
for (int mask = 1; mask < 1 << N; mask++)
{
memset(have, 0, sizeof(have));
add(start);
for (int i = 0; i < N; i++)
if ((mask & 1 << i) == 0)
{
add(inside[i]);
have[open[i]]--;
}
for (int i = 0; i < N; i++)
if ((mask & 1 << i) && have[open[i]] > 0 && possible[mask ^ 1 << i])
{
possible[mask] = true;
first[mask] = i;
break;
}
}
printf("Case #%d: ", test_case);
int mask = (1 << N) - 1;
if (!possible[mask])
{
puts("IMPOSSIBLE");
return;
}
while (mask > 0)
{
printf("%d", first[mask] + 1);
mask ^= 1 << first[mask];
if (mask == 0)
putchar('\n');
else
putchar(' ');
}
}
int main()
{
initialize();
int T; scanf("%d", &T);
for (int tc = 1; tc <= T; tc++)
solve_case(tc);
return 0;
}
For the large dataset:
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <limits>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <utility>
#include <vector>
using namespace std;
const int N_MAX = 205, KEY_MAX = 405;
int K, N, need[N_MAX];
vector<int> inside[N_MAX];
int have[KEY_MAX];
int num_chests[KEY_MAX];
bool opened[N_MAX];
bool visited[N_MAX];
bool edge[N_MAX][N_MAX];
void initialize()
{
}
void open(int chest)
{
assert(!opened[chest]);
opened[chest] = true;
if (chest < N)
{
assert(have[need[chest]] > 0);
have[need[chest]]--;
}
for (int i = 0; i < (int) inside[chest].size(); i++)
have[inside[chest][i]]++;
}
void unopen(int chest)
{
assert(opened[chest]);
opened[chest] = false;
if (chest < N)
have[need[chest]]++;
for (int i = 0; i < (int) inside[chest].size(); i++)
have[inside[chest][i]]--;
}
bool dfs(int num, int original)
{
if (opened[num])
return false;
if (num == original)
return true;
if (visited[num])
return false;
visited[num] = true;
if (original == -1)
original = num;
for (int i = 0; i < N; i++)
if (edge[num][i] && dfs(i, original))
return true;
return false;
}
bool possible()
{
bool copy[N_MAX];
memcpy(copy, opened, sizeof(opened));
bool change = true;
while (change)
{
memset(num_chests, 0, sizeof(num_chests));
change = false;
for (int i = 0; i < N; i++)
if (!opened[i])
num_chests[need[i]]++;
for (int i = 0; i < N; i++)
if (!opened[i] && have[need[i]] >= num_chests[need[i]])
{
open(i);
change = true;
}
memset(visited, false, sizeof(visited));
for (int i = 0; i < N; i++)
if (!opened[i] && (have[need[i]] > 0 && dfs(i, -1)))
{
open(i);
change = true;
}
}
bool answer = true;
for (int i = 0; i < N; i++)
if (!opened[i])
answer = false;
for (int i = 0; i < N; i++)
if (opened[i] && !copy[i])
unopen(i);
return answer;
}
void solve_case(int test_case)
{
scanf("%d %d", &K, &N);
for (int i = 0; i <= N; i++)
inside[i].clear();
for (int i = 0; i < K; i++)
{
int x;
scanf("%d", &x);
inside[N].push_back(x);
}
for (int i = 0; i < N; i++)
{
int n;
scanf("%d %d", &need[i], &n);
for (int j = 0; j < n; j++)
{
int x;
scanf("%d", &x);
inside[i].push_back(x);
}
}
memset(edge, false, sizeof(edge));
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
for (int k = 0; k < (int) inside[i].size(); k++)
if (inside[i][k] == need[j])
edge[i][j] = true;
printf("Case #%d: ", test_case);
memset(have, 0, sizeof(have));
memset(opened, false, sizeof(opened));
open(N);
vector<int> ordering;
for (int it = 0; it < N; it++)
for (int i = 0; i < N; i++)
if (!opened[i] && have[need[i]] > 0)
{
open(i);
if (possible())
{
ordering.push_back(i);
break;
}
else
unopen(i);
}
if ((int) ordering.size() < N)
{
puts("IMPOSSIBLE");
return;
}
for (int i = 0; i < N; i++)
printf("%d%c", ordering[i] + 1, i < N - 1 ? ' ' : '\n');
}
int main()
{
initialize();
int T; scanf("%d", &T);
for (int tc = 1; tc <= T; tc++)
{
solve_case(tc);
fflush(stdout);
}
return 0;
}