Problem:
タイムセール

より良い物を、より安く。今日もどこかのスーパーマーケットで行われるタイムセールでは、激しい闘いが繰り広げられています。ここ会津にある「LL堂」もそんなスーパーマーケットのひとつで、他のチェーン店と対抗すべく、少し変わったタイムセールを実施しています。一般的なタイムセールでは複数の商品が同じ時間に安くなるものですが、LL堂では対象となる商品によって、タイムセールを開始する時間が別々に設定されています。

坂井家はLL堂をよく利用する家庭の一つです。そんな坂井家では、奥様主導のもと、次の日曜に行われるタイムセールに向けて作戦会議を開き、どのような順番で買い物をすれば値引きが最大となるかを分析することになりました。店内を熟知している坂井家は売り場の見取り図を書き、また欲しい品物がどこにあるか、値引きは幾らであるか、売り切れるまでの時間を把握することに成功しました。
ここまでは完璧だった坂井家ですが、分析を行える人がいませんでした。そこで坂井家と親交があったあなたはプログラムを書くことにしました。一人の人が動くことを想定してシミュレーションを行います。

商品は10種類あり、数字1桁の商品番号 g によって 区別されます。タイムセール情報には商品番号 g、 値引き額 d、タイムセールの開始時刻 s と売り切れ時刻 e があります。

店内は横 X、縦 Y のマスで構成される2次元 グリッドで表され、マスごとに通路、商品棚の どちらかが割り当てられています。一つの商品棚には 1種類の商品があり、それは商品番号 g で区別されます。
どの商品を買ってもよいですが、同じ商品番号の商品を複数買ってはいけません。商品棚からは、上下左右に隣接する通路のマスならどんな向きでも商品を取れます。

タイムセールが始まる時刻から商品を取ることができますが、売り切れる時刻からは商品を取ることができません。また、時間は入店した時点で0から始まります。

移動は現在いるマスから上下左右の隣接する通路マスに移動することができますが、商品棚のマスに移動することはできません。グリッドで表される店の外に出ることもできません。１回移動する毎に１単位時間経過します。また、商品を取る時間は考えないものとします。

店内の見取り図と買い物をする人の初期位置と商品のタイムセール情報を入力とし、取ることのできた商品の値引き額の合計の最大値を出力するプログラムを作成してください。

入力

複数のデータセットが与えられます。入力の終わりはゼロふたつの行で示されます。各データセットは以下の形式で与えられます。

X Y
m1,1 m2,1 ... mX,1
m1,2 m2,2 ... mX,2
:
m1,Y m2,Y ... mX,Y
n
g1 d1 s1 e1
g2 d2 s2 e2
:
gn dn sn en

1行目に店舗の大きさX, Y (3 ≤ X, Y ≤ 20) が与えられます。続く Y 行に i 列目 j 行目の店内情報 mi,j が以下の内容で与えられます。

    . (ピリオド) ：通路のマス

    数字 ：商品の番号

    P ：買い物をする人の初期位置のマス

続く行にタイムセール情報の数 n (1 ≤ n ≤ 8) が与えられます。続く n 行に i 番目のタイムセール情報 gi di si ei が与えられます(0 ≤ gi ≤ 9, 1 ≤ di ≤ 10000, 0 ≤ si, ei ≤ 100)。

データセットの数は 50 を超えません。

出力

データセットごとに、取ることのできる商品の値引き額合計の最大値を１行に出力します。

入力例

6 5
1 1 . 0 0 4
1 . . . . .
. . 2 2 . .
. . 2 2 3 3
P . . . . .
5
0 50 5 10
1 20 0 10
2 10 5 15
3 150 3 5
4 100 8 9
0 0

出力例

180

Solution in JAVA (judge verdict: Accepted)

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;



public class Main{
	Scanner sc;
	int INF = 1<<27;
	
	int[][] map;
	int X, Y;
	int N;
	int[] start;
	int[] d_list;
	int[] s_list;
	int[] e_list;
	
	int[][] ofs = { {0, 1}, {0, -1}, {1, 0}, {-1, 0} };
	
	int[][][] dp;
	int[] ans;
	
	void solve(){
		dp = new int[X][Y][1<<N];
		ans = new int[1<<N];
		
		for(int x=0; x<X; ++x)for(int y=0; y<Y; ++y)for(int n=0; n<1<<N; ++n){
			dp[x][y][n] = INF;
		}
		Arrays.fill(ans, -1);
		
		dp[start[0]][start[1]][0] = 0;
		ans[0] = 0;
		bfs(0, start[0], start[1], 0);
		
		for( int from = 0; from < 1<<N; ++from ){
			for( int to = 0; to < N; ++to ){
				if( ((from>>to)&0x01) == 0x00 ){
					LinkedList<ArrayList<Integer>> req = get_req( to );
					Iterator<ArrayList<Integer>> ite = req.iterator();
					
					while(ite.hasNext()){
						ArrayList<Integer> e = ite.next();
						int x = e.get(0);
						int y = e.get(1);
						
						if( dp[x][y][from] < e_list[to] ){
							if( s_list[to] <= dp[x][y][from] ){
								dp[x][y][from | (1<<to)] = Math.min(dp[x][y][from | (1<<to)], dp[x][y][from]);
								bfs(from | (1<<to), x, y, dp[x][y][from | (1<<to)]);
								ans[from | (1<<to)] = ans[from] + d_list[to];
							}else{
								assert( dp[x][y][from] < s_list[to] );
								int d = s_list[to] - dp[x][y][from];
								if( d%2==1 ) ++d;
								assert( s_list[to] <= dp[x][y][from] + d );
								if( dp[x][y][from] + d < e_list[to] ){
									dp[x][y][from | (1<<to)] = Math.min(dp[x][y][from | (1<<to)], dp[x][y][from] + d);
									bfs(from | (1<<to), x, y, dp[x][y][from | (1<<to)]);
									ans[from | (1<<to)] = ans[from] + d_list[to];
								}
							}
						}
					}
				}
			}
		}
		
		int max = -1;
		for(int i=0; i<1<<N; ++i){
			max = Math.max(max, ans[i]);
		}
		assert(max>=0);
		
		System.out.println(max);
	}
	
	void bfs(int n, int sx, int sy, int scost){
		Queue<Integer> qx = new LinkedList<Integer>();
		Queue<Integer> qy = new LinkedList<Integer>();
		
		qx.add(sx);
		qy.add(sy);
		boolean[][] done = new boolean[Y][X];
		done[sy][sx] = true;
		
		while(qx.size()>0){
			int[] p = new int[]{qx.poll(), qy.poll()};
			assert(qx.size() == qx.size());
			
			for( int[] ope : ofs ){
				int[] e = new int[]{p[0], p[1]};
				e[0] += ope[0];
				e[1] += ope[1];
				if( isSafe(e[0], e[1]) ){
					if( !done[e[1]][e[0]] && map[e[1]][e[0]] == -1 ){
						done[e[1]][e[0]] = true;
						dp[e[0]][e[1]][n] = Math.min(dp[e[0]][e[1]][n], dp[p[0]][p[1]][n] + 1);
						qx.add(e[0]);
						qy.add(e[1]);
					}
				}
			}
		}
	}
	
	void run(){
		sc = new Scanner(System.in);
		while(true){
			X = ni();
			Y = ni();
			if((X|Y)==0) break;
			map = new int[Y][X];
			
			start = new int[2];
			for(int y=0; y<Y; ++y)for(int x=0; x<X; ++x){
				char c = sc.next().charAt(0);
				if(c=='.'){
					map[y][x] = -1;
				}else if(c=='P'){
					map[y][x] = -1;
					start[0] = x;
					start[1] = y;
				}else{
					map[y][x] = c - '0';
				}
			}
			
			N = ni();
			d_list = new int[N];
			s_list = new int[N];
			e_list = new int[N];
			for(int i=0; i<N; ++i){
				int g = ni();
				int d = ni();
				int s = ni();
				int e = ni();
				assert(g==i);
				
				d_list[i] = d;
				s_list[i] = s;
				e_list[i] = e;
			}
			
			solve();
		}
		sc.close();
	}
	
	LinkedList<ArrayList<Integer>> get_req(int trg){
		HashSet<ArrayList<Integer>> set = new HashSet<ArrayList<Integer>>();
		for(int y=0; y<Y; ++y)for(int x=0; x<X; ++x){
			int[] e = new int[]{x, y};
			if(map[e[1]][e[0]] == -1){
				for(int[] ope : ofs){
					int[] buf = new int[]{e[0], e[1]};
					buf[0] += ope[0];
					buf[1] += ope[1];
					
					if(isSafe(buf[0], buf[1])){
						if( map[buf[1]][buf[0]] == trg ){
							ArrayList<Integer> p = new ArrayList<Integer>();
							p.add(e[0]);
							p.add(e[1]);
							
							set.add(p);
						}
					}
				}
			}
		}
		
		LinkedList<ArrayList<Integer>> list = new LinkedList<ArrayList<Integer>>();
		Iterator<ArrayList<Integer>> ite = set.iterator();
		while(ite.hasNext()){
			list.add(ite.next());
		}
		
		return list;
	}
	
	int ni(){
		return sc.nextInt();
	}
	
	boolean isSafe(int x, int y){
		return (0<=x && x<X) && (0<=y && y<Y);
	}
	
	public static void main(String[] args){
		new Main().run();
	}
}