USACO 2017 January · Bronze · all three problems
Problem 1 — Don't Be Last!
Statement
Farmer John tracks N milking sessions across his seven cows (Bessie, Elsie, Daisy, Gertie, Annabelle,
Maggie, Henrietta). Each session contributes a positive amount of milk to one named cow; cows that
never appear contribute zero. Output the name of the cow with the second-smallest total milk. If there
is a tie for that position (or every cow ties for least), print Tie.
Constraints
- 1 ≤ N ≤ 100.
- Per-session milk amount is a positive integer ≤ 100.
- Seven fixed cow names.
- Time limit: 2s (Bronze default).
Key idea
Map names to totals with an unordered_map<string,int> seeded at 0 for all 7 cows.
Sort the totals. The answer is the unique cow whose total equals the second-smallest distinct total —
i.e., the smallest total strictly greater than the minimum. If two or more cows share that total, or
every cow ties at the minimum, print Tie.
Complexity target
O(N + 7 log 7). Trivial.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
vector<string> cows = {
"Bessie","Elsie","Daisy","Gertie","Annabelle","Maggie","Henrietta"
};
unordered_map<string,int> total;
for (auto& c : cows) total[c] = 0;
int N; cin >> N;
for (int i = 0; i < N; ++i) {
string name; int m; cin >> name >> m;
total[name] += m;
}
// Find minimum, then the smallest value strictly greater than min.
int mn = INT_MAX;
for (auto& c : cows) mn = min(mn, total[c]);
int second = INT_MAX;
for (auto& c : cows)
if (total[c] > mn) second = min(second, total[c]);
if (second == INT_MAX) { cout << "Tie\n"; return 0; }
string ans; int count = 0;
for (auto& c : cows) if (total[c] == second) { ans = c; ++count; }
cout << (count == 1 ? ans : string("Tie")) << "\n";
}
Pitfalls
- "Second-smallest" means second distinct value, not second sorted entry. Cows tied at the minimum don't move you up the ranking.
- Both tie cases. "All seven tie at the same total" and "two cows share the second total" both produce
Tie. - Cow names are case-sensitive and exactly the seven listed in the statement.
- Missing cows count as 0. Initialize the map for all 7 even if the input never mentions them.
Problem 2 — Hoof, Paper, Scissors
Statement
Two cows play N rounds of a rock-paper-scissors variant. The input lists each round as two integers in {1, 2, 3}, the first cow's gesture and the second cow's. The rules form a 3-cycle (one beats another, that one beats the third, the third beats the first), but Farmer John doesn't know which integer corresponds to which gesture. Compute the maximum number of rounds the first cow can win over all 6 possible mappings of integers ↔ gestures (Hoof, Paper, Scissors).
Constraints
- 1 ≤ N ≤ 100.
- Each gesture is an integer 1, 2, or 3.
- Time limit: 2s.
Key idea
Brute force: try all 6 permutations of (1,2,3) → (H,P,S). For each, count rounds where cow 1's mapped gesture beats cow 2's. Output the max. Equivalent shortcut without permutations: for each ordered pair (a,b) with a ≠ b, count rounds with cow-1 gesture a and cow-2 gesture b; the answer is max over the 3 "winning" orientations of the sum of three such counts — but the 6-permutation brute force is simplest and easily fits in N = 100.
Complexity target
O(6 · N) ≈ 600 ops. Trivial.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int N; cin >> N;
vector<int> a(N), b(N);
for (int i = 0; i < N; ++i) cin >> a[i] >> b[i];
// perm[g] = which gesture (0=H, 1=P, 2=S) integer g+1 maps to.
// Winning relation in (H,P,S) space: H beats S, S beats P, P beats H.
auto beats = [](int x, int y) {
return (x == 0 && y == 2) ||
(x == 2 && y == 1) ||
(x == 1 && y == 0);
};
vector<int> perm = {0, 1, 2};
int best = 0;
do {
int wins = 0;
for (int i = 0; i < N; ++i)
if (beats(perm[a[i] - 1], perm[b[i] - 1])) ++wins;
best = max(best, wins);
} while (next_permutation(perm.begin(), perm.end()));
cout << best << "\n";
}
Pitfalls
- Read the win direction carefully. You want cow 1 to win, not ties or cow 2 wins.
- Brute-forcing all 6 mappings is fine. Don't over-engineer for N ≤ 100.
- Ties don't count as wins. Same integer on both sides is always a tie, regardless of mapping.
Problem 3 — Cow Tipping
Statement
An N×N grid records each cow as 0 (upright) or 1 (tipped). Farmer John has a machine that, when applied at cell (i, j), flips every cell in the upper-left rectangle [1..i] × [1..j]. Compute the minimum number of machine applications needed to turn every cell to 0. (Applying the same rectangle twice cancels out, so each of the N² rectangles is used at most once.)
Constraints
- 1 ≤ N ≤ 10.
- Grid entries are 0 or 1.
- Time limit: 2s.
Key idea
Sweep from the bottom-right corner toward the top-left. At cell (i, j), the only remaining operation that can change it is the one anchored at (i, j) itself (every operation anchored at a (i', j') with i' < i or j' < j has already been decided in this reverse order, and operations anchored further down-right don't touch (i, j)). So if the current value of (i, j) is 1, you must apply the (i, j) rectangle and XOR-flip all cells in [1..i] × [1..j]. This greedy is optimal because the choice at (i, j) is forced.
Complexity target
O(N4) ≈ 10⁴ for N = 10. Plenty.
Reference solution (C++)
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int N; cin >> N;
vector<string> g(N);
for (auto& r : g) cin >> r;
int ops = 0;
// Sweep from (N-1, N-1) up to (0, 0).
for (int i = N - 1; i >= 0; --i) {
for (int j = N - 1; j >= 0; --j) {
if (g[i][j] == '1') {
++ops;
// Flip the entire upper-left rectangle [0..i] x [0..j].
for (int r = 0; r <= i; ++r)
for (int c = 0; c <= j; ++c)
g[r][c] = (g[r][c] == '0') ? '1' : '0';
}
}
}
cout << ops << "\n";
}
Pitfalls
- Sweep direction matters. If you sweep top-left → bottom-right you can't decide the (i, j) rectangle without first knowing future flips. Reverse order makes the decision local.
- Don't simulate "best subset of rectangles" via 2N². Even N = 10 gives 2¹⁰⁰ subsets. The greedy is forced and exact.
- Read input as characters, not ints. The grid is given as N strings of N chars.
What Bronze January 2017 was actually testing
- P1 — careful reading + a hash map. "Second-smallest" with explicit tie handling. No algorithm, all spec.
- P2 — small permutation brute force. 3! mappings, 100 rounds. Recognize when N lets you skip cleverness.
- P3 — forced greedy on a 2D XOR. Reverse-order sweep collapses the search space to a single deterministic path.