1. 程式人生 > >[APIO2013]機器人(斯坦納樹)

[APIO2013]機器人(斯坦納樹)

題目描述

VRI(Voltron 機器人學會)的工程師建造了 n 個機器人。任意兩個相容的機 器人站在同一個格子時可以合併為一個複合機器人。

我們把機器人用 1 至 n 編號(n ≤ 9)。如果兩個機器人的編號是連續的,那 麼它們是相容的,可以合併成一個複合機器人。最初這 n 個機器人各自都只有唯 一的編號。而一個由兩個或以上的機器人合併構成的複合機器人擁有兩個編號, 分別是構成它的所有機器人中最小和最大的編號。

例如,2 號機器人只可以與 1 號或 3 號機器人合併。若 2 號機器人與 3 號機 器人合併,可構成編號為 2-3 的複合機器人。如果編號為 2-3 的複合機器人與編 號為 4-6 的複合機器人合併,可構成編號為 2-6 的複合機器人。當所有機器人合 並以後則構成 1-n 複合機器人。

工程師把這 n 個機器人放在了一個封閉的房間中,房間四周均是牆。該房間 被劃分成 w × h 個方格。有些方格有障礙物,機器人不可經過或停留;其餘方格 允許多個機器人停留,同時允許機器人經過。任何時候一個機器人只佔用一個方 格。初始時刻,所有機器人均在不同的方格中。

這些原始的機器人不會自發地移動。它們只有被工程師沿 x 軸或 y 軸推動 後,才會沿推動的方向不斷向前直線移動,直至碰到障礙物或牆停止移動。停止 移動後,它會掃描當前的格子是否存在可以與它合併的機器人,如果有,則合併 並繼續檢查,直至不能再合併為止。工程師只能沿水平向左、水平向右、豎直向 上、豎直向下四個方向推動機器人,並且,在機器人尚未停止移動時,不允許推 動其它機器人,因此任何時刻,房間中都只能有一個機器人移動。

為了幫助機器人轉向,工程師在一些格子中放置了轉向器。具體地說,轉向 器分為順時針轉向器(右轉器)和逆時針轉向器(左轉器),順時針轉向器可以 使到達該格子的機器人沿順時針方向轉向 90°;逆時針轉向器可以使到達該格 子的機器人沿逆時針方向轉向 90°。

現在,我們將告訴你初始時刻房間內的資訊。請你計算工程師最少共計需要 推動機器人多少次,才能把所有的 n 個機器人全部合併(如果可能的話)。

題解

這道題圖的規模較大,但關鍵點樹較少,容易讓人想到斯坦納樹模型。

一般的斯坦納樹帶著一個點集,但這道題機器人只能合併成一個區間,那麼我們就記dp[i][j][k]表示一個區間為i~j的機器人在k點的最小步數。

然後我們預處理出一個點往四個方向走一步到達的點。

轉移和斯坦納樹一樣,先區間dp一下,在spfa轉移。

細節

調死我了這題,首先預處理dfs時有可能出現環,所以我們要打visit,但是visit一定要記方向!!!!!

搞了個小優化,spfa轉移的時候,先把所有點的dis從小到大排序,在按照順序轉移,於是成功的排到了洛谷rank倒三,bzoj直接TLE。

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
#define N 509
#define R register
using namespace std;
char s[N][N];
int dp[10][10][N*N],h,w,n,lef[5],righ[5],id[N][N],pp,tran[N*N][4],p[N*N];
bool vis[N*N],vi[N*N][4];
int h1,t1,q[N*N],ddf_a,ddf_b;
const int dx[4]={0,0,1,-1};
const int dy[4]={1,-1,0,0};
inline int rd(){
    int x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
inline bool pd(int i,int j){return i<1||j<1||i>h||j>w||s[i][j]=='x';}
inline bool cmp(int a,int b){return dp[ddf_a][ddf_b][a]<dp[ddf_a][ddf_b][b];}
int dfs(int i,int j,int fx){
    if(~tran[id[i][j]][fx])return tran[id[i][j]][fx];
    if(vi[id[i][j]][fx])return 0;
    vi[id[i][j]][fx]=1;
    int xx=i+dx[fx],yy=j+dy[fx];    
    if(pd(xx,yy))return vi[id[i][j]][fx]=0,tran[id[i][j]][fx]=id[i][j];
    if(s[xx][yy]=='A'){
      tran[id[i][j]][fx]=dfs(xx,yy,lef[fx]);
      return vi[id[i][j]][fx]=0,tran[id[i][j]][fx];
    }
    if(s[xx][yy]=='C'){
      tran[id[i][j]][fx]=dfs(xx,yy,righ[fx]);
      return vi[id[i][j]][fx]=0,tran[id[i][j]][fx];
   }
    tran[id[i][j]][fx]=dfs(xx,yy,fx);
    return vi[id[i][j]][fx]=0,tran[id[i][j]][fx];
}
inline void spfa(int x,int y,int s){
    q[h1=t1=1]=s;
    while(h1<=t1){
        int u=q[h1++];vis[u]=0;
        for(R int i=0;i<4;++i){
            int v=tran[u][i];if(v<=0)continue;
            if(dp[x][y][v]>dp[x][y][u]+1){
                dp[x][y][v]=dp[x][y][u]+1;
                if(!vis[v]){vis[v]=1;q[++t1]=v;}
            }
        } 
    }
}
int main(){
    lef[0]=3;lef[3]=1;lef[1]=2;lef[2]=0;
    righ[0]=2;righ[2]=1;righ[1]=3;righ[3]=0;
    n=rd();w=rd();h=rd();
    memset(dp,0x3f,sizeof(dp));
    memset(tran,-1,sizeof(tran));
    for(R int i=1;i<=h;++i)scanf("%s",s[i]+1);
    for(R int i=1;i<=h;++i)
      for(R int j=1;j<=w;++j)if(!pd(i,j)){
          id[i][j]=++pp;
            if(isdigit(s[i][j]))dp[s[i][j]^48][s[i][j]^48][pp]=0;
      }
    for(R int i=1;i<=h;++i)
      for(R int j=1;j<=w;++j)if(!pd(i,j)){
          for(int k=0;k<4;++k){
              tran[id[i][j]][k]=dfs(i,j,k);
          }
      }
    for(R int i=1;i<=pp;++i)p[i]=i;
    for(R int len=1;len<=n;++len)
      for(R int i=1;i+len-1<=n;++i){
          int j=i+len-1;
          for(R int k=1;k<=pp;++k){
               for(R int f=i;f<j;++f)dp[i][j][k]=min(dp[i][j][k],dp[i][f][k]+dp[f+1][j][k]); 
          }
        ddf_a=i;ddf_b=j;
        sort(p+1,p+pp+1,cmp);
        for(R int k=1;k<=pp;++k)spfa(i,j,p[k]);
      }
    int ans=2e9;
    for(R int i=1;i<=pp;++i)ans=min(ans,dp[1][n][i]);
    if(ans==inf)puts("-1");else printf("%d",ans);
    return 0;
}