humblefool-cover
Humblefool Cup

2020 Humblefool Cup Prelims Editorials

ScoringJudges 

Used as: Division One - Level One:

Value

250

Submission Rate

205 / 259 (79.15%)

Success Rate

162 / 205 (79.02%)

High Score

kaun_meet for 249.98 points (0 mins 15 secs)

Average Score

201.61 (for 162 correct submissions)

Sort scores in non-decreasing order. Take the average of first 1/3, and also for the last 1 / 3, and middle elements and sum up.


def overallScore(self, individualScores):
individualScores = list(individualScores)
individualScores.sort()
n = len(individualScores)
t = len(individualScores)/3
fhalf = 0
shalf = 0
for i in range(t):
fhalf += individualScores[i]
shalf += individualScores[n-i-1]
mhalf = sum(individualScores) - fhalf - shalf
fhalf /= float(t)
shalf /= float(t)
mhalf /= float(n - 2*t)

return fhalf + shalf + mhalf

NonSimilarTriangles 

Used as: Division One - Level Two:

Value

500

Submission Rate

163 / 259 (62.93%)

Success Rate

138 / 163 (84.66%)

High Score

kaun_meet for 499.96 points (0 mins 15 secs)

Average Score

409.48 (for 138 correct submissions)

For each triple of sticks of length x, y, z (x <= y < z and x + y > z), divide them by their gcd and add them to a set (which deletes duplications). The answer is the number of elements in the set.

We sort sticks in length to avoid repetition by rotating or mirroring. We divide the length by their gcd to avoid repetitions by uniformly scaling.


def count(self, L):
L=sorted(L)
LEN=len(L)
T=set()

for i in range(LEN):
x=L[i]
for j in range(i+1,LEN):
y=L[j]
G=gcd(x,y)
for k in range(j+1,LEN):
z=L[k]
if z
=x+y:
break
GCD=gcd(G,z)
if (x//GCD,y//GCD,z//GCD) in T:
continue
else:
T.add((x//GCD,y//GCD,z//GCD))
return len(T)

CardDrawPoints 

Used as: Division One - Level Three:

Value

1000

Submission Rate

62 / 259 (23.94%)

Success Rate

42 / 62 (67.74%)

High Score

xiaowuc1 for 976.72 points (4 mins 24 secs)

Average Score

720.43 (for 42 correct submissions)

Consider you are in the middle of the game, having several cards in your hand. You want to decide to terminate the game or not to terminate the game. What matters for your decision? For sure, just the set of cards you picked. As you can not pick the same card twice, we can describe each state with a mask of size n (n bits), where n is size of count[].

Now the solution comes to mind. We are going to use dynamic programming over bitmasks.

What is dp[mask]? Consider we have seen numbers in the mask. Let’s see if it is good to continue or we should terminate the game.

How? By calculating mathematical expectation of the next move. Consider we continue, for each possible card, calculate the score we gain. For example, if i is not present in mask and in the next move the card i is drawn, we gain i scores.

If i is present in mask and the card i is drawn, we lose everything.

Time complexity is O(2^n * n).


double expectedScore(vector <int count) {
for(int i = 0; i < (1<<16); i++) {
dp[i] = 0;
for(int j = 0; j < 16; j++) {
if(i&(1<<j)) dp[i] += j;
}
}
for(int i = 0; i < count.size(); i++) {
has[i] = count[i]
0;
}
for(int mask = -1 + (1 << (count.size())); mask
= 0; mask--) {
bool can = true;
for(int i = 0; i < 16; i++) {
if((mask&(1<<i)) && !has[i]) {
can = false;
break;
}
}
if(!can) continue;
vector<int
newv;
int total = 0;

for(int i = 0; i < count.size(); i++) {
newv.push_back(count[i]);
if(mask&(1<<i)) newv[i]--;
total += newv.back();
}
double draw = 0;
for(int i = 0; i < newv.size(); i++) {
if(mask&(1<<i)) continue;
draw += newv[i] * 1.0 / total * dp[mask | (1 << i)];
}
dp[mask] = max(dp[mask], draw);
}
return dp[0];
}