Problem p01088 (C++) — buggy submission, verdict: Time Limit Exceeded

#include <bits/stdc++.h>
using namespace std;
const int INF = 100110;
int main() {

  while (1) {
    int n;
    cin >> n;
    if (!n)
      break;
    vector<int> a(n);
    for (int i = 0; i < n; i++)
      scanf("%d", &a[i]);

    // i番目までの品物を見て、持ってる小銭がj円の時の{500玉の個数、-消費金額}のmax
    vector<vector<pair<int, int>>> dp(
        n + 1, vector<pair<int, int>>(500 * n + 1000, {-INF, -INF}));
    // vector<vector<vector<int> > > dp(2, vector<vector<int> > (n + 1,
    // vector<int> (500 * n + 1000, INF)));
    dp[0][0] = {0, 0};
    pair<int, int> ans = {0, 0};
    for (int i = 0; i < n; i++) {
      for (int j = 0; j <= 500 * n; j++) {

        if (dp[i][j].first == -INF || dp[i][j].second == -INF)
          continue;
        // cout << i << " " << j << " " << dp[i][j].first << " " <<
        // dp[i][j].second << endl;

        // 買わない
        dp[i + 1][j] = max(dp[i + 1][j], dp[i][j]);

        // 買うんだけども、これに場合分けが生じる
        int r = a[i] % 1000;
        // cout << r << endl;
        if (1 <= r && r <= 500) {
          // 1000札のみで払い、お釣りを受け取るのが最適で、500円玉1枚加算される
          pair<int, int> next = dp[i][j];
          next.first++;
          next.second -= a[i];
          dp[i + 1][j + 500 - r] = max(dp[i + 1][j + 500 - r], next);
        } else if (r == 0) {
          // 小銭があるのなら、500円玉に両替する感覚が適切な可能性あり
          // cout << "ok" << endl;
          pair<int, int> next = dp[i][j];
          next.first++;
          next.second -= a[i];
          if (j - 500 >= 0)
            dp[i + 1][j - 500] = max(dp[i + 1][j - 500], next);
        } else {
          pair<int, int> next = dp[i][j];
          next.second -= a[i];
          // cout << "ok" << endl;
          // 1000札のみを用いて、小銭を増やす作戦
          dp[i + 1][j + 1000 - r] = max(dp[i + 1][j + 1000 - r], next);
          // cout << dp[i + 1][j + 1000 - r].first << " " << dp[i + 1][j + 1000
          // - r].second << endl; 小銭を駆使して、500円玉を入手
          next.first++;
          if (j - r + 500 >= 0)
            dp[i + 1][j - r + 500] = max(dp[i + 1][j - r + 500], next);
          // cout << dp[i + 1][j + r - 500].first << " " << dp[i + 1][j + 1000 -
          // r].second << endl;
        }
      }
    }

    for (int j = 0; j < 500 * n + 1000; j++)
      ans = max(ans, dp[n][j]);
    // cout << dp[1][0].first << " " << dp[1][0].second << endl;
    cout << ans.first << " " << -ans.second << endl;
  }
  return 0;
}


Error: TLE

Fixed submission:

#include <bits/stdc++.h>
using namespace std;
const int INF = 100100100;

int main() {

  while (1) {
    int n;
    cin >> n;
    if (!n)
      break;
    vector<int> a(n);
    for (int i = 0; i < n; i++)
      scanf("%d", &a[i]);

    // i番目までの品物を見て、得られた500円玉がj枚で持ってる小銭がk円の時の消費金額の最小値
    vector<vector<vector<int>>> dp(
        2, vector<vector<int>>(n + 1, vector<int>(500 * n + 1000, INF)));
    dp[0][0][0] = 0;
    int now = 0;
    int next = 1;
    for (int i = 0; i < n; i++) {
      for (int j = 0; j <= i; j++) {
        for (int k = 0; k <= 500 * i; k++) {

          int r = a[i] % 1000;
          // 買わない
          dp[next][j][k] = min(dp[next][j][k], dp[now][j][k]);

          // 1000で割ったあまりによる場合わけ
          if (1 <= r && r <= 500) {
            // 1000札のみで払うのが最適
            // 500円獲得
            dp[next][j + 1][(500 - r) + k] =
                min(dp[next][j + 1][(500 - r) + k], dp[now][j][k] + a[i]);
          } else {
            // 小銭を駆使して500円玉を獲得
            int need = r - 500;
            if (need == -500)
              need = 500;
            if (k - need >= 0)
              dp[next][j + 1][k - need] =
                  min(dp[next][j + 1][k - need], dp[now][j][k] + a[i]);
            else {
              // 1000円札のみで払う
              // 500円玉は獲得できない
              dp[next][j][(1000 - r) % 1000 + k] =
                  min(dp[next][j][(1000 - r) % 1000 + k], dp[now][j][k] + a[i]);
            }
          }
        }
      }
      swap(now, next);
    }

    int ans1 = 0, ans2 = INF;
    for (int j = n; j >= 0; j--) {
      for (int k = 0; k <= 500 * n; k++) {
        if (dp[now][j][k] != INF) {
          if (ans1 < j || (ans1 == j && ans2 > dp[now][j][k])) {
            ans1 = j;
            ans2 = dp[now][j][k];
          }
        }
      }

      if (ans1 != 0)
        break;
    }

    cout << ans1 << " " << ans2 << endl;
  }
  return 0;
}