AOJ2021 Princess in Danger
問題リンク Princess in Danger
- 解法
基本的には(都市、血液の残り時間)をノードにしたダイクストラです。が、このままだとTLEになります。
工夫1. 評価関数を導入します。「(都市、時間)の値」+「(都市-ゴール)間の最短距離」が小さい順に頂点を訪問します。これは、その状態から少なくとも、ゴールまでの最短距離だけ時間がかかるので、評価関数として使えます。任意の2頂点間の最短距離はワーシャルフロイド法で求めておきます。
工夫2. 辺は隣接リストで表現する
工夫3. 頂点の状態は1つの整数にしておく
工夫4. ノード(i, m)にDで到達したとする。このとき、m' (0<= m' <=m-1)の状態において、D以上のコストがかかっているノード(i, m')があった場合、その状態から始まる探索をしなくてよい。なぜなら、残り時間がより大きいmにそれらより安いコストで到達しているのだから、(i, m)から探索した結果の方が必ず良いに決まっているからです。ただ、これをチェックするのにO(m)かかるのであまり効果的とはいえないかも・・・
Javaだとこれだけの工夫をすればギリギリ間に合います (TimeLimit 16秒に対して15.95秒で通りました 汗)
他のJavaerの方々はどう解いているのでしょうかねぇ
- ソース
import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.PriorityQueue; import java.util.Scanner; //Princess in Danger public class AOJ2021 { int[][] dist, wf; int INF = 1<<29, P = 107; int N, M, L, K, A, H; class E{ int t, d; public E(int t, int d) { this.t = t; this.d = d; } } @SuppressWarnings("unchecked") void run(){ Scanner sc = new Scanner(System.in); for(;;){ N = sc.nextInt(); M = sc.nextInt(); L = sc.nextInt(); K = sc.nextInt(); A = sc.nextInt(); H = sc.nextInt(); if((N|M|L|K|A|H)==0)break; boolean[] ice = new boolean[N]; ice[A] = ice[H] = true; while(L--!=0)ice[sc.nextInt()] = true; List<E>[] es = new ArrayList[N]; for(int i=0;i<N;i++)es[i]=new ArrayList<E>(); wf = new int[N][N]; for(int[]a:wf)Arrays.fill(a, INF); while(K--!=0){ int s = sc.nextInt(), t = sc.nextInt(), d = sc.nextInt(); wf[s][t] = wf[t][s] = d; es[s].add(new E(t, d)); es[t].add(new E(s, d)); } for(int k=0;k<N;k++)for(int i=0;i<N;i++)for(int j=0;j<N;j++)wf[i][j]=Math.min(wf[i][j], wf[i][k]+wf[k][j]); dist = new int[N][M+1]; for(int[]a:dist)Arrays.fill(a, INF); for(int i=0;i<=M;i++)dist[A][i] = 0; PriorityQueue<Integer> q = new PriorityQueue<Integer>(N, new Comparator<Integer>() { public int compare(Integer o1, Integer o2) { int a = o1/P, b = o1%P, c = o2/P, d = o2%P; return (dist[a][b]+wf[a][H])-(dist[c][d]+wf[c][H]); } }); q.add(A*P+M); int res = INF; while(!q.isEmpty()){ int v = q.poll(); int pos = v/P, m = v%P; if(pos==H){ res = dist[pos][m]; break; } for(E e:es[pos]){ for(int t=0;t+m<=M;t++){ if(t>0&&!ice[pos])break; int nm = t+m-e.d; if(nm<0)continue; int w = dist[pos][m]+t+e.d; if(w<dist[e.t][nm]){ q.remove(e.t*P+nm); dist[e.t][nm] = w; int r = nm-1; while(r>=0&&w<=dist[e.t][r]){ q.remove(e.t*P+r); dist[e.t][r] = w; r--; } q.add(e.t*P+nm); } } } } System.out.println(res==INF?"Help!":res); } } public static void main(String[] args) { new AOJ2021().run(); } }