zoukankan      html  css  js  c++  java
  • 2020CCPC东北四省赛 L.PepperLa's Express(曼哈顿距离的性质)

    题意:

    给定一个三维整点空间,空间中的点分为三类:空地,用户,分配站。一个用户到分配站的距离定义为两者的曼哈顿距离,一个用户的贡献定义为他到所有分配站的最短距离。问再在一个空地上加入一个分配站可以使得所有用户贡献的最小值为多少?

    思路:

    二分答案,只要考虑当前贡献大于答案的那些用户即可。

    该题的关键在于曼哈顿距离的特殊性质,两个点的曼哈顿距离可以表示成和两点之间相对位置无关的一个形式:

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

    因此,如果多次询问一个点到给定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();
        }
    }
    
  • 相关阅读:
    第一章——第二节 启动模式
    Android 展示键盘时候布局被修改的问题
    JAVA混型和潜在类型机制
    第一章——Activity的生命周期
    android 程序中禁止屏幕旋转和重启Activity
    项目知识—九
    项目知识——八
    项目知识——七
    Drawable复习—第六章
    项目知识(六)
  • 原文地址:https://www.cnblogs.com/ucprer/p/14063335.html
Copyright © 2011-2022 走看看