Problem p03160 (C++)

Buggy submission (status: Time Limit Exceeded):
#include <bits/stdc++.h>

using namespace std;

#define INF (int)(1e9)

// The recurrence we will be optimizing is
// c(n) = min(c(n - 1) + cost(n, n - 1),
//            c(n - 2) + cost(n, n - 2))

// h[i] is the "value" of stone i
// cost(i, j) = abs(h[i] - h[j])

// I think this way is more natural
int solve_backwards(const vector<int> &h, int N) {
  if (N < 0)
    return INF;
  if (N == 0)
    return 0;

  return min(solve_backwards(h, N - 1) + abs(h[N] - h[N - 1]),
             solve_backwards(h, N - 2) + abs(h[N] - h[N - 2]));
}

int solve_forwards(const vector<int> &h, int N) {
  if ((size_t)N >= h.size())
    return INF;
  if ((size_t)N == h.size() - 1)
    return 0;

  return min(solve_forwards(h, N + 1) + abs(h[N] - h[N + 1]),
             solve_forwards(h, N + 2) + abs(h[N] - h[N + 2]));
}

// position -> cost
// the shortest path to i will inform us about the shortest
// path to get to i + 1 and i + 2
// this will solve to the shortest path to N in general
// This is really just adding a cache to the original
// brute force recurrence
// Easiest way to apply DP in general
map<int, int> map_dp;
int solve_memo(const vector<int> &h, int N) {
  if (map_dp.find(N) != map_dp.end())
    return map_dp[N];

  if (N < 0)
    return INF;
  if (N == 0)
    return 0;

  return min(solve_memo(h, N - 1) + abs(h[N] - h[N - 1]),
             solve_memo(h, N - 2) + abs(h[N] - h[N - 2]));
}

// Same idea as last time
// Instead of doing it top down, we will do it bottom up
// Building up smaller values of this optimization first and using
// them to find the answer for N
// This is just changing the order
// You need to know the order of your computations for this to work.
// This case is easy. Generally this means topologically sorting your
// computations
// If this is hard, you may just want to do something like solve_memo.
int solve_tab(const vector<int> &h, int N) {
  vector<int> dp(N);

  // Initial conditions
  dp[0] = 0;
  // This is here because so
  // the recurrence doesn't reach too
  // far back with indices
  dp[1] = abs(h[0] - h[1]);

  // Original recurrence
  for (int i = 2; i <= N; ++i) {
    dp[i] =
        min(dp[i - 1] + abs(h[i] - h[i - 1]), dp[i - 2] + abs(h[i] - h[i - 2]));
  }

  return dp[N - 1];
}

int main() {

  cin.tie(NULL);
  ios_base::sync_with_stdio(false);

  int N;
  cin >> N;
  vector<int> h(N);
  for (auto &e : h) {
    cin >> e;
  }
  // cout << solve_backwards(h, N - 1) << '\n';
  // cout << solve_forwards(h, 0) << '\n';
  cout << solve_memo(h, N - 1) << '\n';
  // cout << solve_tab(h, N) << '\n';

  return 0;
}


Fixed submission:
#include <bits/stdc++.h>

using namespace std;

#define INF (int)(1e9)

// The recurrence we will be optimizing is
// c(n) = min(c(n - 1) + cost(n, n - 1),
//            c(n - 2) + cost(n, n - 2))

// h[i] is the "value" of stone i
// cost(i, j) = abs(h[i] - h[j])

// I think this way is more natural
int solve_backwards(const vector<int> &h, int N) {
  if (N < 0)
    return INF;
  if (N == 0)
    return 0;

  return min(solve_backwards(h, N - 1) + abs(h[N] - h[N - 1]),
             solve_backwards(h, N - 2) + abs(h[N] - h[N - 2]));
}

int solve_forwards(const vector<int> &h, int N) {
  if ((size_t)N >= h.size())
    return INF;
  if ((size_t)N == h.size() - 1)
    return 0;

  return min(solve_forwards(h, N + 1) + abs(h[N] - h[N + 1]),
             solve_forwards(h, N + 2) + abs(h[N] - h[N + 2]));
}

// position -> cost
// the shortest path to i will inform us about the shortest
// path to get to i + 1 and i + 2
// this will solve to the shortest path to N in general
// This is really just adding a cache to the original
// brute force recurrence
// Easiest way to apply DP in general
map<int, int> map_dp;
int solve_memo(const vector<int> &h, int N) {
  if (map_dp.find(N) != map_dp.end())
    return map_dp[N];

  if (N < 0)
    return INF;
  if (N == 0)
    return 0;

  return map_dp[N] = min(solve_memo(h, N - 1) + abs(h[N] - h[N - 1]),
                         solve_memo(h, N - 2) + abs(h[N] - h[N - 2]));
}

// Same idea as last time
// Instead of doing it top down, we will do it bottom up
// Building up smaller values of this optimization first and using
// them to find the answer for N
// This is just changing the order
// You need to know the order of your computations for this to work.
// This case is easy. Generally this means topologically sorting your
// computations
// If this is hard, you may just want to do something like solve_memo.
int solve_tab(const vector<int> &h, int N) {
  vector<int> dp(N);

  // Initial conditions
  dp[0] = 0;
  // This is here because so
  // the recurrence doesn't reach too
  // far back with indices
  dp[1] = abs(h[0] - h[1]);

  // Original recurrence
  for (int i = 2; i <= N; ++i) {
    dp[i] =
        min(dp[i - 1] + abs(h[i] - h[i - 1]), dp[i - 2] + abs(h[i] - h[i - 2]));
  }

  return dp[N - 1];
}

int main() {

  cin.tie(NULL);
  ios_base::sync_with_stdio(false);

  int N;
  cin >> N;
  vector<int> h(N);
  for (auto &e : h) {
    cin >> e;
  }
  // cout << solve_backwards(h, N - 1) << '\n';
  // cout << solve_forwards(h, 0) << '\n';
  cout << solve_memo(h, N - 1) << '\n';
  // cout << solve_tab(h, N) << '\n';

  return 0;
}