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();
    }
}
posted @ 2020-11-30 20:11  UCPRER  阅读(489)  评论(0编辑  收藏  举报