Codeforces 83 C. Track

問題

hxwマスのグリッドのそれぞれに、a-zの文字またはS,Tが書かれている。


SからTのマスへのパスを考えたとき、パス上にある文字をつなげた文字列をsとする。
sに現れるアルファベットの数がk個以下で、最小の長さをもつようなsを求めよ。
複数ある場合は辞書順で最小のものを求めよ。

制約条件

h,w≦50
k≦4

方針

使う文字を限定して探索などをするとTLEになる。
A*探索をする。
ゴールまでのコストの見積りは、現在の文字列の長さ+ゴールまでのマンハッタン距離。


見積りコストが同点の場合は文字列で比較してやるようにする。
すると最初にゴールを見つけたものが辞書順最小になる。
50msくらいで通る。

ソースコード

typedef pair<int,pair<pair<string,int>,int> > node;
const int dy[]={-1,0,1,0}, dx[]={0,-1,0,1};
inline int D(int x1,int y1,int x2,int y2){ return abs(x1-x2)+abs(y1-y2); }

int h,w,k;
string in[50];


void run(){
  cin>>h>>w>>k;
  int sy,sx,gy,gx;
  rep(i,h){
    cin>>in[i];
    rep(j,w){
      if(in[i][j]=='S')sy=i, sx=j;
      else if(in[i][j]=='T')gy=i, gx=j;
    }
  }
  set<pi> s;
  priority_queue<node,vector<node>,greater<node> > q;
  q.push(mp(D(sy,sx,gy,gx),mp(mp("",0),sy*w+sx)));
  while(!q.empty()){
    int y=q.top().second.second/w, x=q.top().second.second%w;
    string str=q.top().second.first.first;
    int mask=q.top().second.first.second;
    q.pop();
    if(s.count(mp(mask,y*w+x)))continue;
    s.insert(mp(mask,y*w+x));
    
    rep(d,4){
      int ny=y+dy[d], nx=x+dx[d];
      if(ny<0||nx<0||ny>=h||nx>=w||ny==sy&&nx==sx)continue;
      if(ny==gy&&nx==gx){
        cout<<str<<endl; return;
      }
      string next=str+in[ny][nx];
      int nmask=mask|1<<in[ny][nx]-'a';
      if(__builtin_popcount(nmask)<=k)
        q.push(mp(D(ny,nx,gy,gx)+next.size(),mp(mp(next,nmask),ny*w+nx)));
    }
  }
  cout<<-1<<endl;
}