0530 Pyon-Pyon River Crossing (ぴょんぴょん川渡り)

http://rose.u-aizu.ac.jp/onlinejudge/ProblemSet/description.jsp?id=0530
足場の位置とすべりやすさが与えられ,向こう岸に辿りつくルートのうち危険度の合計が最小になるときの値を求める問題.


最初はグラフとみて Dijkstra 法で解いて 00:19 sec,2988 KB で AC した.

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <limits>
using namespace std;

struct state {
  int sum_danger;
  int danger;
  int row, col;
  int jump;
  state(int s, int d, int r, int c, int j) : sum_danger(s), danger(d), row(r), col(c), jump(j) {}
  bool operator<(const state& rhs) const
  {
    return sum_danger > rhs.sum_danger;
  }
};

int main()
{
  int N, M;
  while (cin >> N >> M && N != 0) {
    vector<vector<pair<int,int> > > g(N);
    for (int i = 0; i < N; i++) {
      int k;
      cin >> k;
      for (int j = 0; j < k; j++) {
        int c, d;
        cin >> c >> d;
        g[i].push_back(make_pair(c, d));
      }
    }

    vector<vector<vector<int> > > tbl(N);
    for (int i = 0; i < N; i++) {
      tbl[i] = vector<vector<int> >(g[i].size(), vector<int>(M+1, numeric_limits<int>::max()));
    }
    priority_queue<state> q;
    for (int i = 0; i < g[0].size(); i++) {
      q.push(state(0, g[0][i].second, 0, g[0][i].first, 0));
      tbl[0][i][0] = 0;
    }
    for (int i = 0; i < g[1].size(); i++) {
      q.push(state(0, g[1][i].second, 1, g[1][i].first, 1));
      tbl[1][i][1] = 0;
    }

    int ans = numeric_limits<int>::max();
    while (!q.empty()) {
      const state s = q.top();
      q.pop();

      if (s.row+1 == N) {
        ans = min(ans, s.sum_danger);
        continue;
      }
      for (int i = 0; i < g[s.row+1].size(); i++) {
        const int next_col = g[s.row+1][i].first;
        const int next_danger = g[s.row+1][i].second;
        const int d = s.sum_danger + (s.danger + next_danger) * abs(next_col - s.col);
        if (d < tbl[s.row+1][i][s.jump]) {
          tbl[s.row+1][i][s.jump] = d;
          q.push(state(d, next_danger, s.row+1, next_col, s.jump));
        }
      }
      if (s.jump < M && s.row+2 <= N) {
        if (s.row+2 == N) {
          ans = min(ans, s.sum_danger);
        } else {
          for (int i = 0; i < g[s.row+2].size(); i++) {
            const int next_col = g[s.row+2][i].first;
            const int next_danger = g[s.row+2][i].second;
            const int d = s.sum_danger + (s.danger + next_danger) * abs(next_col - s.col);
            if (d < tbl[s.row+2][i][s.jump+1]) {
              tbl[s.row+2][i][s.jump+1] = d;
              q.push(state(d, next_danger, s.row+2, next_col, s.jump+1));
            }
          }
        }
      }
    }
    cout << ans << endl;
  }
  return 0;
}


しかし問題の制約上戻ることは無いので DP で解ける.
それでもう一度 submit して 00:07 sec,1404 KB に改善した.

#include <iostream>
#include <vector>
#include <algorithm>
#include <limits>
using namespace std;

int main()
{
  int N, M;
  while (cin >> N >> M && N != 0) {
    vector<vector<pair<int,int> > > g(N);
    for (int i = 0; i < N; i++) {
      int k;
      cin >> k;
      for (int j = 0; j < k; j++) {
        int c, d;
        cin >> c >> d;
        g[i].push_back(make_pair(c, d));
      }
    }

    vector<vector<vector<int> > > dp(N);
    for (int i = 0; i < N; i++) {
      dp[i].resize(g[i].size(), vector<int>(M+1, numeric_limits<int>::max()/2));
    }
    for (int i = 0; i < g[0].size(); i++) {
      dp[0][i][0] = 0;
    }
    for (int i = 0; i < g[1].size(); i++) {
      dp[1][i][1] = 0;
    }

    int ans = numeric_limits<int>::max();
    for (int i = 0; i < N; i++) {
      for (int j = 0; j < g[i].size(); j++) {
        for (int m = 0; m <= M; m++) {
          if (i+1 == N) {
            ans = min(ans, dp[i][j][m]);
          } else {
            for (int k = 0; k < g[i+1].size(); k++) {
              dp[i+1][k][m] = min(dp[i+1][k][m], dp[i][j][m] + (g[i][j].second + g[i+1][k].second)*abs(g[i][j].first - g[i+1][k].first));
            }

            if (i < N-1 && m < M) {
              if (i+2 == N) {
                ans = min(ans, dp[i][j][m]);
              } else {
                for (int k = 0; k < g[i+2].size(); k++) {
                  dp[i+2][k][m+1] = min(dp[i+2][k][m+1], dp[i][j][m] + (g[i][j].second + g[i+2][k].second)*abs(g[i][j].first - g[i+2][k].first));
                }
              }
            }
          }
        }
      }
    }
    cout << ans << endl;
  }
  return 0;
}