AOJ0245 Time Sale
問題リンク Time Sale
- 解法
メモ化再帰で解くのが方針です。
dp[ i ][ j ][ S ]: 商品の状態がSで(i, j)にいるための最小時間
という表を埋めて解きます。Sは、手に取った商品の番号のビットが立っているような整数です。
この問題の厄介な部分は、移動しないと時間が経過しないことだと思います。なので、商品xを手に取れる棚に、セールが始まる時間以前に辿りついたとしても、この商品を手に取ることができるとは限りません。その場に留まって待つということができないからです。隣接するマスを行き来することで時間2の間隔で行ったり来たりできます。なので、最短到達時間 + 2*k の時間のどれかが、表を埋めるための最小値となります。往復している間に売り切れてしまったら、商品xはゲットできません。
なお、表を埋めるために、任意のマス間の最短距離が必要になります。頂点数が高々400なのでワーシャルフロイドだと時間が厳しいです。幅優先探索をH*W回実行した方が速くなります。
自分はこの方針でごり押ししてなんとか時間内に解けましたが、他の人はすごい速いですね。
- ソース
import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; import java.util.Set; //Time Sale public class AOJ0245 { int h, w, INF = 1<<29, n, si, sj; int[][] d = {{-1,0},{0,1},{1,0},{0,-1}}; int[][] a, wf; int[][][] dp; int[] s, f, dis, sum; Set<Integer>[] set; int get(int i, int j, int S){ if(dp[i][j][S]!=-1)return dp[i][j][S]; if(S==0)return INF; int min = INF; for(int last=0;last<10;last++)if(((S>>last)&1)>0){ if(!set[last].contains(i*w+j))continue; int sub = S-(1<<last); if(sub==0){ int m = wf[i*w+j][si*w+sj]; while(m < f[last]){ if(s[last]<=m)break; m+=2; } if(f[last]<=m)m = INF; min = m; break; } for(int p=0;p<h*w;p++){ if(a[p/w][p%w]!=-1)continue; int m = get(p/w, p%w, sub) + wf[i*w+j][p]; while(m < f[last]){ if(s[last]<=m)break; m+=2; } if(f[last]<=m)m = INF; min = Math.min(min, m); } } return dp[i][j][S] = min; } @SuppressWarnings("unchecked") void run(){ dp = new int[20][20][1024]; a = new int[20][20]; dis = new int[10]; s = new int[10]; f = new int[10]; wf = new int[400][400]; sum = new int[1024]; Scanner sc = new Scanner(System.in); for(;;){ w = sc.nextInt(); h = sc.nextInt(); if((w|h)==0)break; si = -1; sj = -1; for(int i=0;i<h;i++)for(int j=0;j<w;j++){ char c = sc.next().charAt(0); if(c=='P'){ si = i; sj = j; c = '.'; } a[i][j] = c=='.'?-1:(c-'0'); } Arrays.fill(dis, 0); Arrays.fill(s, 0); Arrays.fill(f, 0); n = sc.nextInt(); for(int i=0;i<n;i++){ int g = sc.nextInt(); dis[g] = sc.nextInt(); s[g] = sc.nextInt(); f[g] = sc.nextInt(); } set = new Set[10]; for(int i=0;i<10;i++)set[i]=new HashSet<Integer>(); for(int i=0;i<h;i++)for(int j=0;j<w;j++){ if(a[i][j]!=-1)continue; for(int k=0;k<4;k++){ int ni = i+d[k][0], nj = j+d[k][1]; if(0<=ni&&ni<h&&0<=nj&&nj<w&&a[ni][nj]!=-1)set[a[ni][nj]].add(i*w+j); } } Arrays.fill(sum, 0); for(int S=0;S<1024;S++)for(int j=0;j<10;j++)if(((S>>j)&1)>0)sum[S]+=dis[j]; for(int i=0;i<h;i++)for(int j=0;j<w;j++){ int b = i*w+j; for(int p=0;p<h*w;p++)wf[b][p] = INF; Queue<Integer> q = new LinkedList<Integer>(); wf[b][b] = 0; q.add(b); while(!q.isEmpty()){ int V = q.poll(); for(int k=0;k<4;k++){ int ni = V/w+d[k][0], nj = V%w+d[k][1]; if(0<=ni&&ni<h&&0<=nj&&nj<w&&a[ni][nj]==-1&&wf[b][ni*w+nj]==INF){ wf[b][ni*w+nj] = wf[b][V]+1; q.add(ni*w+nj); } } } } for(int i=0;i<h;i++)for(int j=0;j<w;j++)for(int S=0;S<1024;S++)dp[i][j][S]=-1; dp[si][sj][0] = 0; int res = 0; for(int i=0;i<h;i++)for(int j=0;j<w;j++){ if(a[i][j]!=-1)continue; for(int S=1023;S>0;S--){ if(sum[S]<=res)continue; if(get(i, j, S)<INF)res = sum[S]; } } System.out.println(res); } } public static void main(String[] args) { new AOJ0245().run(); } }