AOJ 2151 Brave Princess Revisited

AOJ 2151 Brave Princess Revisited

解法

宿と予算が少ない! ということで動的計画法で解こうとしました
(トポロジカルソートできていないのでDPじゃない)。

設計メモ

dp[現在地][残りの予算] : 最小化された盗賊らの人数
d[出発地][到着地] = 出発地と到着地の距離(コスト)
e[出発地][到着地] = 出発地と到着地の間に出てくる盗賊らの人数

dp = INF
for( n 回 )
  for( i : 出発地 )
    for( j : 到着地 )
      for( k : 出発地時点の予算 )
        if 予算が足りる
          // 予算を使う
          dp[j][ k - d[i][j] ] = min( dp[j][ k - d[i][j] ], dp[i][ k ] )
        end
        // 予算を使わない
        dp[j][ k ] = min( dp[j][ k ], dp[i][ k ] + e[i][j] )

一番最初は、番号が戻ってくること(サンプルの最後)を忘れていて
書き直さなきゃ〜と思ったのですが、一番多く戻るパターンでも
n - 3回しかないことに気付けて、ひどい方法ですがその分
ループさせてます(やっぱり怖いのでn回回しています...)。
O(n^3 l)で最大10^8くらいなので大丈夫そう。
こんな感じで、あとは欲しい処理、変数を足していきます。

ソースコード

import java.util.Arrays;
import java.util.Scanner;

public class Main {
  Scanner sc;

  void run() {
    for ( ;; ) {
      int n = ni();
      int m = ni();
      int l = ni();
      if ( ( n | m | l ) == 0 ) {
        break;
      }

      int[][] dm = new int[n + 1][n + 1];
      int[][] em = new int[n + 1][n + 1];
      boolean[][] lk = new boolean[n + 1][n + 1];

      for ( int i = 0; i < m; ++i ) {
        int a = ni();
        int b = ni();
        int d = ni();
        int e = ni();
        dm[ a ][ b ] = dm[ b ][ a ] = d;
        em[ a ][ b ] = em[ b ][ a ] = e;
        lk[ a ][ b ] = lk[ b ][ a ] = true;
      }

      boolean[][] ok = new boolean[n + 1][l + 1];
      int[][] dp = new int[n + 1][l + 1];
      for ( int[] v : dp )
        Arrays.fill( v, 1 << 28 );
      dp[ 1 ][ l ] = 0;
      ok[ 1 ][ l ] = true;
      for ( int h = 0; h < n; ++h ) {
        for ( int i = 1; i <= n; ++i ) {
          for ( int j = 1; j <= n; ++j ) {
            if ( lk[ i ][ j ] ) {
              for ( int k = 0; k <= l; ++k ) {
                if ( ok[ i ][ k ] ) {
                  if ( k - dm[ i ][ j ] >= 0 ) {
                    dp[ j ][ k - dm[ i ][ j ] ] = Math.min( dp[ j ][ k
                        - dm[ i ][ j ] ], dp[ i ][ k ] );
                    ok[ j ][ k - dm[ i ][ j ] ] = true;
//                    debug( i, j, k - dm[ i ][ j ], dp[ j ][ k - dm[ i ][ j ] ] );
                  }
                  dp[ j ][ k ] = Math.min( dp[ j ][ k ], dp[ i ][ k ]
                      + em[ i ][ j ] );
                  ok[ j ][ k ] = true;
//                  debug( "a", i, j, k, dp[ j ][ k ] );
                }
              }
            }
          }
        }
      }

      int min = 1 << 28;
      for ( int k = 0; k <= l; ++k ) {
        min = Math.min( min, dp[ n ][ k ] );
      }

      System.out.println( min );
    }
  }

  Main() {
    sc = new Scanner( System.in );
  }

  int ni() {
    return sc.nextInt();
  }

  public static void main(String[] args) {
    new Main().run();
  }

  void debug(Object... os) {
    System.err.println( Arrays.deepToString( os ) );
  }
}

終わりに

通ってよかった...がスマートじゃない...。
他の方の解法を見ていると優先度付きキューを用いた
ダイクストラで解いているみたいですね。
動的計画法がようやく書けるようになってきたので、
書きたくて書きたくて仕様がないです
(問題見たとき最初にまず「ん? コレ動的計画法で解くんじゃね?」ってなるくらい...)💦
目的と手段がごっちゃにならないように気をつけます。