Computing the nth Fibonacci number using recursion (fib(n) = fib(n-1)+fib(n-2)) takes time at least 2n/2. We improve this by noting that this computation is highly redundant. For example, fib(3) is recomputed many times. To see this, look at the recursion tree for, say, fib(8):
To improve this, we can use dynamic programming. This is implemented one of two ways:
For details, see S04_CS141:FibonacciByDP .
For more examples: S04_CS141:DynamicProgramming
Generally, the difficult part of dynamic programming is figuring out what class of subproblems to look at. Here, the thinking is very similar to what you need when you are trying to apply any divide and conquer approach (e.g. from mergesort). You identify smaller or simpler problems such that, if you had those solved, you could solve the given problem easily. Then you recursively ask what subproblems you would need to solve to solve those subproblems, and so on.
If you get stuck figuring out how to "divide and conquer", consider small concrete examples of the problem, try brute force enumeration (by hand), and look for patterns.
Finally, the running time of your algorithm will be something like