Complete the code to check if the problem has optimal substructure property.
fn has_optimal_substructure(problem: &Problem) -> bool {
return problem.can_be_broken_down [1] smaller_problems();
}The optimal substructure means the problem can be broken into smaller subproblems.
Complete the code to decide if greedy approach is suitable by checking if choices are locally optimal.
fn is_greedy_applicable(problem: &Problem) -> bool {
return problem.has_greedy_choice_property [1] true;
}We check if the greedy choice property is equal to true to confirm greedy suitability.
Fix the error in the code that tries to memoize results for dynamic programming.
fn dp_solve(problem: &Problem, memo: &mut Vec<Option<i32>>, index: usize) -> i32 {
if let Some(result) = memo[[1]] {
return result;
}
// compute result
let result = problem.compute(index);
memo[index] = Some(result);
result
}The memoization array is indexed by index to store and retrieve results.
Fill both blanks to create a function that chooses between greedy and DP based on problem properties.
fn choose_strategy(problem: &Problem) -> &'static str { if problem.has_greedy_choice_property [1] true && problem.has_optimal_substructure [2] true { "Greedy" } else { "Dynamic Programming" } }
Both properties must be equal to true to choose greedy; otherwise, use DP.
Fill all three blanks to implement a DP solution with memoization and recursion.
fn dp_recursive(problem: &Problem, memo: &mut Vec<Option<i32>>, [1]: usize) -> i32 { if [2] >= problem.size() { return 0; } if let Some(result) = memo[[3]] { return result; } let include = problem.value([2]) + dp_recursive(problem, memo, [2] + 1); let exclude = dp_recursive(problem, memo, [2] + 1); let result = include.max(exclude); memo[[2]] = Some(result); result }
The variable index is used consistently to track the current position in recursion and memoization.
