Identify a Counterfeit Coin from Three Weighings
National Olympiad — National round (2020 season)
Submission name: scale.cpp
Problem summary
One coin among A…L is lighter or heavier. From three equal-count pan weighings, identify the unique coin and weight direction, when possible.
Input
Three lines: left coin string,right coin string,outcome <,>,=. Here < means the right pan rises and > means it descends.
Output
Coin and lighter/heavier if unique, otherwise indefinitely.
Constraints
Twelve coins and three weighings, with equal coin counts on both pans.
Worked example
Input and output for the worked example discussed below.
Input
ABCD EFGH = ABCI EFJK < ABIJ EFGH =
Output
K lighter
Solution analysis
- Enumerate all 24 hypotheses: each of twelve coins may have deviation −1 or +1. Genuine weights cancel because each weighing uses equal pan counts.
- For one hypothesis compute left deviation minus right deviation. Positive means the right pan rises(<), negative means it descends(>), zero means balance. Retain only hypotheses matching all three.
- In the sample only K lighter fits: its presence in the second right pan makes that pan rise, and the other two weighings balance. Multiple surviving identities or signs mean identification is indefinite.
Why it works
The enumeration contains every possible counterfeit identity and sign and rejects precisely inconsistent hypotheses.
Watch out. Interpret symbols as the stated right-pan direction, not numeric less-than of left-pan weight. The sign must also be unique.
Commented C++17 solution
An independently written complete program with standard headers.
// The enumeration contains every possible counterfeit identity and sign and rejects precisely inconsistent
// hypotheses.
#include<algorithm>
#include<array>
#include<iostream>
#include<string>
#include<utility>
#include<vector>
using namespace std;
struct Weighing{
string left,right;
char outcome;
};
int main(){
array<Weighing,3>tests;
for(Weighing&test:tests){
cin>>test.left>>test.right>>test.outcome;
}
vector<pair<char,int>>possible;
for(char coin='A';coin<='L';coin++){
for(int sign:{-1,1}){
bool valid=true;
for(const Weighing&test:tests){
int delta=sign*
(int(count(test.left.begin(),test.left.end(),coin))-
int(count(test.right.begin(),test.right.end(),coin)));
char result=delta>0?'<':delta<0?'>':'=';
valid=valid&&result==test.outcome;
}
if(valid){
possible.push_back({coin,sign});
}
}
}
if(possible.size()==1){
cout<<possible[0].first<<' '<<(possible[0].second<0?"lighter":"heavier")<<'\n';
}
else{
cout<<"indefinitely\n";
}
}