1. 程式人生 > 實用技巧 >2020CCPC東北四省賽 L.PepperLa's Express(曼哈頓距離的性質)

2020CCPC東北四省賽 L.PepperLa's Express(曼哈頓距離的性質)

題意:

給定一個三維整點空間,空間中的點分為三類:空地,使用者,分配站。一個使用者到分配站的距離定義為兩者的曼哈頓距離,一個使用者的貢獻定義為他到所有分配站的最短距離。問再在一個空地上加入一個分配站可以使得所有使用者貢獻的最小值為多少?

思路:

二分答案,只要考慮當前貢獻大於答案的那些使用者即可。

該題的關鍵在於曼哈頓距離的特殊性質,兩個點的曼哈頓距離可以表示成和兩點之間相對位置無關的一個形式:

\[\operatorname{dis}(i, j)=a b s\left(x_{i}-x_{j}\right)+a b s\left(y_{i}-y_{j}\right)+a b s\left(z_{i}-z_{j}\right)\\\operatorname{dis}(i, j)=\max \left\{\begin{array}{l}\left(x_{i}-x_{j}\right)+\left(y_{i}-y_{j}\right)+\left(z_{i}-z_{j}\right) \\\left(x_{i}-x_{j}\right)+\left(y_{i}-y_{j}\right)+\left(z_{j}-z_{i}\right) \\\left(x_{i}-x_{j}\right)+\left(y_{j}-y_{i}\right)+\left(z_{i}-z_{j}\right) \\\left(x_{i}-x_{j}\right)+\left(y_{j}-y_{i}\right)+\left(z_{j}-z_{i}\right) \\\left(x_{j}-x_{i}\right)+\left(y_{i}-y_{j}\right)+\left(z_{i}-z_{j}\right) \\\left(x_{j}-x_{i}\right)+\left(y_{i}-y_{j}\right)+\left(z_{j}-z_{i}\right) \\\left(x_{j}-x_{i}\right)+\left(y_{j}-y_{i}\right)+\left(z_{i}-z_{j}\right) \\\left(x_{j}-x_{i}\right)+\left(y_{j}-y_{i}\right)+\left(z_{j}-z_{i}\right)\end{array}\right.\\\operatorname{dis}(i, j)=\max \left\{\begin{array}{l}\left(+x_{i}+y_{i}+z_{i}\right)+\left(-x_{j}-y_{j}-z_{j}\right) \\\left(+x_{i}+y_{i}-z_{i}\right)+\left(-x_{j}-y_{j}+z_{j}\right) \\\left(+x_{i}-y_{i}+z_{i}\right)+\left(-x_{j}+y_{j}-z_{j}\right) \\\left(+x_{i}-y_{i}-z_{i}\right)+\left(-x_{j}+y_{j}+z_{j}\right) \\\left(-x_{i}+y_{i}+z_{i}\right)+\left(+x_{j}-y_{j}-z_{j}\right) \\\left(-x_{i}+y_{i}-z_{i}\right)+\left(+x_{j}-y_{j}+z_{j}\right) \\\left(-x_{i}-y_{i}+z_{i}\right)+\left(+x_{j}+y_{j}-z_{j}\right) \\\left(-x_{i}-y_{i}-z_{i}\right)+\left(+x_{j}+y_{j}+z_{j}\right)\end{array}\right. \]

因此,如果多次詢問一個點到給定n個點的最大曼哈頓距離,只要將n個點的上述8個值算出來,並分別取最大,就可以O(1)得到答案了

每次二分答案的檢驗方法,就是遍歷圖中的所有空地,查詢到標記使用者的最大距離的最小值是否<=答案即可

程式碼:

#include <bits/stdc++.h>
using namespace std;
const int maxn=105;
const int INF=1e9;
char c[maxn][maxn][maxn];
struct node{
    int z,x,y;
};
int z,x,y;
int dis[maxn][maxn][maxn];
int vis[maxn][maxn][maxn];
int xx[]={-1,1,0,0,0,0};
int yy[]={0,0,-1,1,0,0};
int zz[]={0,0,0,0,-1,1};
int sgn[]={-1,1};
void init(){
    for(int i=1;i<=z;i++)
        for(int j=1;j<=x;j++)
            for(int k=1;k<=y;k++)
                dis[i][j][k]=INF,vis[i][j][k]=0;
}
void bfs(){
    init();
    queue<node>q;
    for(int i=1;i<=z;i++)
        for(int j=1;j<=x;j++)
            for(int k=1;k<=y;k++)
                if(c[i][j][k]=='@')
                    q.push({i,j,k}),dis[i][j][k]=0,vis[i][j][k]=1;
    while(!q.empty()){
        node u=q.front();
        q.pop();
        for(int i=0;i<6;i++){
            int tz=u.z+zz[i],tx=u.x+xx[i],ty=u.y+yy[i];
            if(tz>=1 && tz<=z && tx>=1 && tx<=x && ty>=1 && ty<=y){
                if(!vis[tz][tx][ty]){
                    vis[tz][tx][ty]=1;
                    dis[tz][tx][ty]=dis[u.z][u.x][u.y]+1;
                    q.push({tz,tx,ty});
                }
            }
        }
    }
}
bool check(int mid){
    vector<node>v;
    for(int i=1;i<=z;i++)
        for(int j=1;j<=x;j++)
            for(int k=1;k<=y;k++)
                if(c[i][j][k]=='*'&&dis[i][j][k]>mid)
                    v.push_back({i,j,k});
    if(v.empty())return 1;
    int maxx[8];
    for(int i=0;i<8;i++)maxx[i]=-INF;
    for(auto u:v){
        for(int p=0;p<8;p++)
            maxx[p]=max(maxx[p],u.z*sgn[p&1]+u.x*sgn[p>>1&1]+u.y*sgn[p>>2&1]);
    }
    int ans=INF;//和*的最大距離的最小值
    for(int i=1;i<=z;i++)
        for(int j=1;j<=x;j++)
            for(int k=1;k<=y;k++){
                if(c[i][j][k]!='.')continue;
                int temp[8];
                for(int p=0;p<8;p++)
                    temp[p]=i*sgn[p&1]+j*sgn[p>>1&1]+k*sgn[p>>2&1];
                int maxd=0;//和所有*的最大距離
                for(int p=0;p<8;p++){
                    maxd=max(maxd,temp[p]+maxx[p^7]);
                }
                ans=min(ans,maxd);
            }
    return ans<=mid;
}
void solve(){
    bfs();
    int l=0,r=z+x+y;//zkyb
    while(r-l>1){
        int mid=(l+r)>>1;
        if(check(mid)){
            r=mid;
        }
        else{
            l=mid;
        }
    }
    cout<<r<<endl;
}
int main () {
    ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
    while(cin>>z>>x>>y){
        for(int i=1;i<=z;i++)
            for(int j=1;j<=x;j++)
                cin>>(c[i][j]+1);
        solve();
    }
}