ferinの競プロ帳

競プロについてのメモ

AOJ 0530 Pyon-Pyon River Crossing

問題ページ
ぴょんぴょん川渡り | Aizu Online Judge

考えたこと

制約が色々小さくて最小コストを求めるので拡張dijkstraやDPを考える。 dp[i][j][k] = (i行目j列目まででk回一行飛ばしをしたときの最小滑りやすさ) としてDPを考えるとO(NMmax(k)^2)で計算量的には問題なさそう。岸から石に移るときの処理がうまくまとめられなかったので例外処理をする。こういうときはコメントを書くなり紙で整理してから実装しないとバグらせるので気をつけながらDPを書くと通った。
問題文が長くて実装面倒でICPCっぽさを感じた。

//#define __USE_MINGW_ANSI_STDIO 0
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef vector<int> VI;
typedef vector<VI> VVI;
typedef vector<ll> VL;
typedef vector<VL> VVL;
typedef pair<int, int> PII;

#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define ALL(x) x.begin(), x.end()
#define IN(a, b, x) (a<=x&&x<b)
#define MP make_pair
#define PB push_back
const int INF = (1LL<<30);
const ll LLINF = (1LL<<60);
const double PI = 3.14159265359;
const double EPS = 1e-12;
const int MOD = 1000000007;
//#define int ll

template <typename T> T &chmin(T &a, const T &b) { return a = min(a, b); }
template <typename T> T &chmax(T &a, const T &b) { return a = max(a, b); }

int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};

vector<PII> stone[155];
int dp[155][15][100];
signed main(void)
{
  while(true) {
  int n, m;
  cin >> n >> m;
  if(!n) break;
  REP(i, n) {
    int k;
    cin >> k;
    stone[i].clear();
    REP(j, k) {
      int x, d;
      cin >> x >> d;
      stone[i].PB({x, d});
    }
  }
  memset(dp, 0x3f, sizeof(dp));
  REP(i, stone[0].size()) dp[0][i][0] = 0;
  REP(i, stone[1].size()) dp[1][i][1] = 0;
  // i行目j列 一行飛ばしをk回やった
  REP(i, n-1) REP(j, stone[i].size()) REP(k, m+1) {
    if(dp[i][j][k] == 0x3f3f3f3f) continue;
    // (i, j)から一行飛ばししないとき
    // dp[i+1][l][k] を minで更新
    REP(l, stone[i+1].size()) {
      int dif = abs(stone[i][j].first - stone[i+1][l].first);
      int tmp = dif*(stone[i][j].second + stone[i+1][l].second);
      chmin(dp[i+1][l][k], dp[i][j][k] + tmp);
    }

    // (i, j)から一行飛ばしするとき
    // dp[i+2][l][k+1] を minで更新
    if(i == n-2 || k == m) continue;
    REP(l, stone[i+2].size()) {
      int dif = abs(stone[i][j].first - stone[i+2][l].first);
      int tmp = dif*(stone[i][j].second + stone[i+2][l].second);
      chmin(dp[i+2][l][k+1], dp[i][j][k] + tmp);
    }
  }

  // n-1行目のmin、n-2行目でm-1以下のmin
  int ret = INF;
  REP(i, stone[n-1].size()) REP(k, m+1) chmin(ret, dp[n-1][i][k]);
  REP(i, stone[n-2].size()) REP(k, m) chmin(ret, dp[n-2][i][k]);
  cout << ret << endl;
  }

  return 0;
}