RUPC (Ritsumeikan University Programming Contest) 2011 Problem D The Legendary Sword
問題
hxwマスのグリッドに、数字で表される珠が置かれている。
スタートのマスはS、ゴールのグリッドはGである。
ゴールの前に全ての種類の珠に最低一度ずつ触れる必要がある。
珠は、1番の珠→2番の珠と、数字の小さい順に触れなければならない。
同じ数字の珠はどれか一つのみ触れればよい。
ゴールするのにかかる、最短の移動距離を求めよ。
移動は、隣接する上下左右のマスにすることができ、グリッドの外へ出ることはできない。
制約条件
h≦100, w≦100
方針
数字がiである珠のj個目(同じ数字の珠を区別するために、左上から適当に1番、2番とつける)を触るのに必要な、最短の移動距離をdp[i][j]とする。
次に触れる珠はi+1番目の珠のどれかなので、
dp[i+1][k] = min(dp[i][j] + (珠i,jから珠i+1,kへの距離))の漸化式が成り立つ。
この漸化式にしたがって表を更新するようなDPをすればよい。
ソースコード
const int dy[]={-1,0,1,0},dx[]={0,-1,0,1}; string maze[100][101]; int dp[100][100]; int main() { int h,w; while(cin>>w>>h,w){ int sy,sx,gy,gx,maxtama=0; rep(i,h)rep(j,w){ cin>>maze[i][j]; if(maze[i][j]=="S")sy=i, sx=j; if(maze[i][j]=="G")gy=i, gx=j; if(isdigit(maze[i][j][0]))maxtama=max(maxtama,atoi(maze[i][j].c_str())); } vector<vi> tamas(maxtama+2); rep(i,h)rep(j,w)if(isdigit(maze[i][j][0])){ int num=atoi(maze[i][j].c_str()); tamas[num].pb(i*w+j); } tamas[0].pb(sy*w+sx); tamas[maxtama+1].pb(gy*w+gx); vector<vi> dp(maxtama+2); rep(i,maxtama+2){ dp[i].resize(tamas[i].size()); rep(j,dp[i].size())dp[i][j]=inf; } dp[0][0]=0; rep(i,maxtama+1)rep(j,dp[i].size())rep(k,dp[i+1].size()){ dp[i+1][k]=min(dp[i+1][k],dp[i][j]+ abs(tamas[i][j]/w-tamas[i+1][k]/w)+abs(tamas[i][j]%w-tamas[i+1][k]%w)); } cout<<dp[maxtama+1][0]<<endl; } return 0; }