Investigating in How Many Ways Objects of Two Different Colours Can Be Grouped
How many ways can 60 black objects and 40 white objects be grouped into non-empty groups, where the order of groups does not matter and each group is characterized by the pair (b, w) denoting the n...
Problem Statement
This archive keeps the full statement, math, and original media on the page.
Having three black objects B and one white object W they can be grouped in 7 ways like this: \[(BBBW) \quad (B,BBW) \quad (B,B,BW) \quad (B,B,B,W) \quad (B,BB,W) \quad (BBB,W) \quad (BB,BW)\] In how many ways can sixty black objects B and forty white objects W be thus grouped?
Problem 181: Investigating in How Many Ways Objects of Two Different Colours Can Be Grouped
Mathematical Development
Definition 1. A bicolour partition of the pair is a multiset of elements from satisfying
Let denote the number of such bicolour partitions. We seek .
Theorem 1 (Generating Function). The bicolour partition function satisfies
Proof. The set of admissible part types is . A bicolour partition is a function assigning a multiplicity to each part type, subject to and . The generating function for the multiplicity of a single part type is the geometric series
Since the part types are independent, the total generating function is the product over all part types. The coefficient of this product counts the number of multisets with the prescribed totals, which is precisely .
Theorem 2 (Unbounded Knapsack Reduction). Let the part types be enumerated in an arbitrary fixed total order as where . Define as the number of bicolour partitions of using only part types from . Then , for , and for each with :
Proof. By the principle of sequential decision: for each part type , choose its multiplicity , then count partitions of the residual using only the preceding part types. The base case reflects the empty partition of .
Corollary 1 (In-Place Computation). If part types are processed sequentially and, for each part type , the table is updated via
with ranging from to and from to in increasing order, then after processing all part types, .
Proof. This is the standard unbounded knapsack transformation. Iterating in increasing order permits each part type to be used with unlimited multiplicity. The in-place update telescopes the summation in Theorem 2, since after processing part , the entry equals . The proof proceeds by induction on : the base case holds by initialization; for the inductive step, the forward-sweep update accumulates exactly the sum .
Editorial
Different Colours Can Be Grouped Count the number of bicolour partitions of (60, 40), i.e., the number of unordered groupings of 60 black and 40 white objects into non-empty groups. Uses two-dimensional unbounded knapsack DP.
Pseudocode
Set dp[0..B][0..W] <- 0
Set dp[0][0] <- 1
For db from 0 to B:
For dw from 0 to W:
If db = 0 and dw = 0 then continue
For b from db to B:
For w from dw to W:
dp[b][w] += dp[b - db][w - dw]
Return dp[B][W]
Complexity Analysis
- Time: The outer double loop iterates over part types. For each part type , the inner loop performs updates. Hence the total work is . For , this yields approximately operations.
- Space: for the two-dimensional DP table.
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#include <bits/stdc++.h>
using namespace std;
int main() {
const int B = 60, W = 40;
vector<vector<long long>> dp(B + 1, vector<long long>(W + 1, 0));
dp[0][0] = 1;
for (int db = 0; db <= B; db++) {
for (int dw = 0; dw <= W; dw++) {
if (db == 0 && dw == 0) continue;
for (int b = db; b <= B; b++) {
for (int w = dw; w <= W; w++) {
dp[b][w] += dp[b - db][w - dw];
}
}
}
}
cout << dp[B][W] << endl;
return 0;
}
"""
Project Euler Problem 181: Investigating in How Many Ways Objects of Two
Different Colours Can Be Grouped
Count the number of bicolour partitions of (60, 40), i.e., the number of
unordered groupings of 60 black and 40 white objects into non-empty groups.
Uses two-dimensional unbounded knapsack DP.
"""
def solve(B=60, W=40):
dp = [[0] * (W + 1) for _ in range(B + 1)]
dp[0][0] = 1
for db in range(B + 1):
for dw in range(W + 1):
if db == 0 and dw == 0:
continue
for b in range(db, B + 1):
for w in range(dw, W + 1):
dp[b][w] += dp[b - db][w - dw]
return dp[B][W]
if __name__ == "__main__":
answer = solve()
assert answer == 83735848679360680
print(answer)