Dice Combinations
You are given an integer \(n\). You repeatedly throw a fair 6-sided die (values \(1..6\)) and sum up the results.
Your task is to count the number of ways to obtain total sum exactly \(n\) by throwing the die one or more times.
Order matters: For example, for \(n = 3\), the valid sequences are:
- \(1 + 1 + 1\)
- \(1 + 2\)
- \(2 + 1\)
- \(3\)
So the answer is \(4\).
Input:
- A single integer \(n\).
Output:
- Print the number of ways (modulo \(10^9 + 7\)).
Constraints:
- \(1 \le n \le 10^6\)
Example:
Input:
3
Output:
4
Solution
This is a classic 1D dynamic programming problem.
Let’s use the SRTBOT framework to design the DP.
S – State
Define:
- \(dp[x] = \text{number of ways to obtain sum } x \text{ using dice throws}.\)
We want to compute dp[n].
R – Recurrence (Relation)
To get sum \(x\), the last die roll could be:
- 1 → previous sum = \(x-1\)
- 2 → previous sum = \(x-2\)
- …
- 6 → previous sum = \(x-6\)
So every way to make \(x\) is formed by:
a way to make \((x-k)\) and then rolling a \(k\), for some \(k \in {1,2,3,4,5,6}\).
Therefore:
- \(dp[x] = dp[x-1] + dp[x-2] + dp[x-3] + dp[x-4] + dp[x-5] + dp[x-6],\)
where we only include terms with \((x-k) \ge 0\). All operations are done modulo \(10^9+7\).
T – Transitions
From smaller sums to bigger sums:
For each \(x\), we look back at up to 6 previous states:
- \(x \leftarrow x-1, x-2, \dots, x-6\).
These are the edges in our DP graph.
B – Base cases
What about \(dp[0]\)?
- There is exactly one way to make sum 0: do nothing (empty sequence).
So:
dp[0] = 1- For \(x < 0\), we treat
dp[x] = 0(no ways to get a negative sum).
All other dp[x] will be computed from the recurrence.
O – Order of computation
We must compute dp[x] in increasing order of x, because dp[x] depends only on smaller sums:
- Start from
dp[0]. - Then compute
dp[1], dp[2], ..., dp[n]in order.
dp[0] = 1;
for x in 1..n:
dp[x] = sum of dp[x-k] for k in 1..6, x-k >= 0T – Time and Space Complexity
For each x from 1 to n, we consider up to 6 transitions:
- Time: \(O(6n) = O(n)\)
- Space: \(O(n)\) for the
dparray of sizen+1.
This is fine for \(n \le 10^6\).
Intuition recap
- Break the problem at the last die roll.
- State = “number of ways to get a certain sum”.
- Each state depends on up to 6 previous sums.
- We build answers from
0up ton.
#include <iostream>
#include <vector>
using namespace std;
const int MOD = 1'000'000'007;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> dp(n + 1, 0);
dp[0] = 1; // one way to make sum 0: empty sequence
for (int x = 1; x <= n; x++) {
long long ways = 0;
for (int k = 1; k <= 6; k++) {
if (x - k >= 0) {
ways += dp[x - k];
if (ways >= MOD) ways -= MOD; // avoid overflow
}
}
dp[x] = (int)ways;
}
cout << dp[n] << '\n';
return 0;
}Modulo and types
- Always take results mod \(10^9+7\).
- Use
long longwhile summing (to avoid overflow), then cast back toint. - The DP array can be
vector<int>since we always store values< MOD.