You are given an array of length N, where each element i represents the number of ways we can produce i units of change. For example, [1, 0, 1, 1, 2] would indicate that there is only one way to make 0, 2, or 3 units, and two ways of making 4 units.
Given such an array, determine the denominations that must be in use. In the case above, for example, there must be coins with value 2, 3, and 4.
Input: [1,0,1,1,2]Output: [2,3,4]Explanation:
- Index 0:1 way to make 0(using no coins)- Index 1:0 ways to make 1(no coin of value 1)- Index 2:1 way to make 2(using coin of value 2)- Index 3:1 way to make 3(using coin of value 3)- Index 4:2 ways to make 4(using coin of value 4, or using coin of value 2 twice)
Input: [1,1,1,1,1]Output: [1]Explanation:
With coin of value 1, we can make any amount with exactly 1 way:-0: use no coins
-1: use one coin of value 1-2: use two coins of value 1-3: use three coins of value 1-4: use four coins of value 1
Input: [1,0,1,0,1,1,2]Output: [2,4,5]Explanation:
-0:1way(no coins)-1:0ways(no coin of value 1)-2:1way(coin of value 2)-3:0ways(cannot be made with coins 2,4,5)-4:1way(coin of value 4, or two coins of value 2)-5:1way(coin of value 5)-6:2ways(coin 2+ coin 4, or three coins of value 2)
Input: [1,1,2,2,3]Output: [1,2]Explanation:
With coins of value 1 and 2:-0:1way(no coins)-1:1way(one coin of value 1)-2:2ways(one coin of value 2, or two coins of value 1)-3:2ways(one coin of value 2+ one coin of value 1, or three coins of value 1)-4:3ways(two coins of value 2, one coin of value 2+ two coins of value 1, or four coins of value 1)
Input: [1,0,0,1]Output: [3]Explanation:
Only coin of value 3 exists:-0:1way(no coins)-1:0ways(cannot make with coin 3)-2:0ways(cannot make with coin 3)-3:1way(one coin of value 3)
We can detect coin denominations by analyzing the change in the number of ways to make different amounts. When we introduce a new coin denomination, it creates additional ways to make certain amounts. If ways[i] > ways[i-d] where d is a potential denomination, then d is likely a valid coin denomination.
classSolution {
public: vector<int> findDenominations(vector<int>& ways) {
int n = ways.size();
vector<int> denominations;
// Check each potential denomination
for (int d =1; d < n; d++) {
if (ways[d] >0) {
// Check if d could be a valid denomination
bool isValid = true;
vector<int> dp(n, 0);
dp[0] =1;
// Add current denominations
for (int coin : denominations) {
for (int i = coin; i < n; i++) {
dp[i] += dp[i - coin];
}
}
// Add potential denomination d
for (int i = d; i < n; i++) {
dp[i] += dp[i - d];
}
// Check if adding d makes the dp match ways up to current index
bool matches = true;
for (int i =0; i <= d && i < n; i++) {
if (dp[i] != ways[i]) {
matches = false;
break;
}
}
if (matches) {
denominations.push_back(d);
}
}
}
return denominations;
}
};
classSolution {
public List<Integer>findDenominations(int[] ways) {
int n = ways.length;
List<Integer> denominations =new ArrayList<>();
// Check each potential denominationfor (int d = 1; d < n; d++) {
if (ways[d]> 0) {
// Check if d could be a valid denominationint[] dp =newint[n];
dp[0]= 1;
// Add current denominationsfor (int coin : denominations) {
for (int i = coin; i < n; i++) {
dp[i]+= dp[i - coin];
}
}
// Add potential denomination dfor (int i = d; i < n; i++) {
dp[i]+= dp[i - d];
}
// Check if adding d makes the dp match ways up to current indexboolean matches =true;
for (int i = 0; i <= d && i < n; i++) {
if (dp[i]!= ways[i]) {
matches =false;
break;
}
}
if (matches) {
denominations.add(d);
}
}
}
return denominations;
}
}
classSolution:
deffindDenominations(self, ways: List[int]) -> List[int]:
n = len(ways)
denominations = []
# Check each potential denominationfor d in range(1, n):
if ways[d] >0:
# Check if d could be a valid denomination dp = [0] * n
dp[0] =1# Add current denominationsfor coin in denominations:
for i in range(coin, n):
dp[i] += dp[i - coin]
# Add potential denomination dfor i in range(d, n):
dp[i] += dp[i - d]
# Check if adding d makes the dp match ways up to current index matches =Truefor i in range(min(d +1, n)):
if dp[i] != ways[i]:
matches =Falsebreakif matches:
denominations.append(d)
return denominations
We can optimize by building the solution incrementally. For each position, we check if it could be a new denomination by seeing if it explains the increase in ways compared to what we can already make with existing denominations.
classSolution {
public List<Integer>findDenominationsOptimized(int[] ways) {
int n = ways.length;
List<Integer> denominations =new ArrayList<>();
int[] dp =newint[n];
dp[0]= 1;
for (int i = 1; i < n; i++) {
if (dp[i]< ways[i]) {
// i must be a denomination denominations.add(i);
// Update dp array with new denominationfor (int j = i; j < n; j++) {
dp[j]+= dp[j - i];
}
}
}
return denominations;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
classSolution:
deffindDenominationsOptimized(self, ways: List[int]) -> List[int]:
n = len(ways)
denominations = []
dp = [0] * n
dp[0] =1for i in range(1, n):
if dp[i] < ways[i]:
# i must be a denomination denominations.append(i)
# Update dp array with new denominationfor j in range(i, n):
dp[j] += dp[j - i]
return denominations