[BZOJ] 最长距离

问题描述

windy 有一块矩形土地,被分为 NM 块 11 的小格子。 有的格子含有障碍物。如果从格子 A 可以走到格子 B,那么两个格子的距离就为两个格子中心的欧几里德距离。如果从格子 A 不可以走到格子 B,就没有距离。 如果格子 X 和格子 Y 有公共边,并且 X 和 Y 均不含有障碍物,就可以从 X 走到 Y。 如果 windy 可以移走 T 块障碍物,求所有格子间的最大距离。 保证移走 T 块障碍物以后,至少有一个格子不含有障碍物。

输入格式

第一行包含三个整数,N M T。
接下来有 N 行,每行一个长度为 M 的字符串,'0'表示空格子,'1'表示该格子含有障碍物。

输出格式

输出包含一个浮点数,保留 6 位小数。

样例输入输出

输入输出样例 1

Input
3 3 0
001
001
110
Output
1.414214

输入输出样例 2

Input
4 3 0
001
001
011
000
Output
3.605551

输入输出样例 3

Input
3 3 1
001
001
001
Output
2.828427

数据范围

20%的数据,满足 1 <= N,M <= 30 ; 0 <= T <= 0 。
40%的数据,满足 1 <= N,M <= 30 ; 0 <= T <= 2 。
100%的数据,满足 1 <= N,M <= 30 ; 0 <= T <= 30 。

解析

可以发现,题目的重点在于最长的欧氏距离而不是删哪T个点,那么只要求出两个相距最远的点使其之间的路径最少经过的障碍点的数量小于T即可。接下来的问题是如何求出两点之间最少经过的障碍点。联想到最短路,对每个点跑一边Dijkstra即可。

代码

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <cmath>
#include <iomanip>
#define N 32
#define M 902
using namespace std;
int head[M],ver[M*4],nxt[M*4],edge[M*4],ll;
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
char c[N];
int n,m,t,i,j,k,l,a[N][N],f[M][M];
bool vis[M];
void insert(int x,int y,int z)
{
    ll++;
    ver[ll]=y;
    edge[ll]=z;
    nxt[ll]=head[x];
    head[x]=ll;
}
bool in(int x,int y)
{
    return x<=n&&x>=1&&y<=m&&y>=1;
}
int get(int x,int y)
{
    return (x-1)*n+y;
}
void Dijkstra(int s)
{
    priority_queue<pair<int,int> > q;
    memset(vis,0,sizeof(vis));
    q.push(make_pair(0,s));
    while(!q.empty()){
        int x=q.top().second;
        q.pop();
        if(vis[x]) continue;
        vis[x]=1;
        for(int i=head[x];i;i=nxt[i]){
            int y=ver[i];
            if(f[s][y]>f[s][x]+edge[i]){
                f[s][y]=f[s][x]+edge[i];
                q.push(make_pair(-f[s][y],y));
            }
        }
    }
}
double dis(int x1,int y1,int x2,int y2)
{
    return sqrt(1.0*(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int main()
{
    memset(f,0x3f,sizeof(f));
    cin>>n>>m>>t;
    for(i=1;i<=n;i++){
        cin>>c;
        for(j=0;j<m;j++) a[i][j+1]=c[j]-'0';
    }
    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            for(k=0;k<4;k++){
                int x=i+dx[k],y=j+dy[k];
                if(in(x,y)) insert(get(i,j),get(x,y),a[x][y]);
            }
        }
    }
    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            int x=get(i,j);
            f[x][x]=a[i][j];
            Dijkstra(x);
        }
    }
    double ans=0;
    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            for(k=1;k<=n;k++){
                for(l=1;l<=m;l++){
                    int x=get(i,j),y=get(k,l);
                    if(x!=y&&f[x][y]<=t) ans=max(ans,dis(i,j,k,l));
                }
            }
        }
    }
    cout<<setprecision(6)<<fixed<<ans<<endl;
    return 0;
}

转载于:https://www.cnblogs.com/LSlzf/p/11415319.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值