HuNan Province Multi-School Competition(2014-04-13) Problem K-Cliff Walk Solution

Problem : 

 在某个盛夏之夜,桑海之滨,zhexipinnong仰望着星空,皎洁的月光,凉爽的海风......突然, 啊! Help me!!  Help! .....H..e...l.....p.......。 zhexipinnong被潮汐卷走了。

 另一个故事又开始了: little_w家在一个H x W的网格型(每个格子大小为10 x 10m)的坎坷的沙滩上,坐标为(XY),她喜欢在沙滩上散步。有这么一些规则:

①每个格子的高度是固定的,海潮随时间变化,可近似为:v =  meter , t 单位为second。 其中a是随季节变化的系数。

little_w只会走退潮后晒了至少1 hour的地方,且只能往东南西北四个方向走,当然也可以在原地享受。

little_w走向的位置和当前位置高度差不能超过1 meter,从一个格子走向另一个格子时需用时m second , 且整个过程中两块区域都必须是晒干了的。

little_w自由活动时间只有12hour,因为little_mother会叫她回家吃饭。

现在little_w想知道她能到达且可以按时回家的位置距离她家最多有多少meter? 计算时以格子中心为标准。

Input:

First line :     a   m        // 0.0<a<15.0 , 0.1<=m<=60.0

Second line:   W  H  X  Y  // 1<=W,H<=200. 

Follow H lines each containing W integers, describing the height in millimetres.         //  0<=h<=20000. 

Output:

Little_w 能到达的最远位置离她家的距离(meter)

 

Solution:

难点:(1)篇幅很长,英语阅读理解能力。

           (2)怎样判断一个点是否可达,并能按时安全返回?

      整体思路是比较每个点最早能到达的时刻arrive[i]和最晚必须离开的时刻left[i] , arrive[i] <= left , 则可以走到第i,并按时返回。为什么呢?

分析:从y=的图形可看出:如果能够待在某个点,一定是一个完整的时间段。因此: 如果在arrive[i]left[i]两个时刻待在第i点是可行的,且arrive[i] <= left[i] ,那么在arrive[i]left[i]之间的任意时刻都是待在第i个点上都是安全的。

 

 

时间单位统一为 s , 高度单位统一为 mm.

 

我们可以先算出每个点没被海水淹没的时间段:

开始 : Ta = 6.0*3600/pi * acos(2.0*h/a - 1.0)

结束 : Tb = 12*3600 - Ta.

然后从(X,Y)0时刻做一次单源最短路可求得arrive[].

(X,Y)12*3600时刻做一次逆向的单源最短路可求得left[i].

具体细节参考代码:


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <cstdlib>
#include <cassert>
using namespace std;
typedef long double LD ;

const int maxn = 205 * 205;
const LD INF = (LD)1e10 ,eps = (LD)1e-8 , pi =(LD) acosl(-1.0) ;
const int dx[] = {0,-1,0,1};
const int dy[] = {1,0,-1,0};
int sig(LD x){  return (x>eps) - (x<-eps) ; }

struct Edge{
    int from,to ;
    LD L , R ;
    Edge(){};
    Edge(int a,int b,LD l,LD r):from(a) , to(b) , L(l) ,R(r){};
};

LD a , m  ;
int W , H , X , Y ;
LD height[maxn];

LD Ta[maxn] , Tb[maxn] ;
LD arrive[maxn]  , left[maxn];
vector<int>G[maxn];
vector<Edge>edges;

void add_edge(int u,int v,LD l,LD r){
    edges.push_back(Edge(u,v,l,r)) ;
    G[u].push_back(edges.size() - 1);
}
inline int ID(int a ,int b){
    return a*W+b ;
}
inline LD cal_time(LD h){ // milimetres , return second
    if(h > a) return -INF ;
    return 6.0*3600/pi * acosl(2.0*h/a - 1.0) ;
}

void build_graph(){
    for(int i=0; i<H ;i++) for(int j=0; j<W ;j++){
        int id = ID(i,j) ;
        arrive[id] = INF , left[id] = -INF ;
        LD t = cal_time(height[id]) ;
        Ta[id] = t , Tb[id] = 12.0*3600 - t ;
    }
    for(int x=0; x<H; x++) for(int y=0; y<W;y++) for(int d=0;d<4;d++) {
        int nx = x+dx[d] , ny = y+dy[d] ;
        int u = ID(x,y) , v = ID(nx ,ny) ;
        if(nx<0 || nx>=H || ny<0 || ny>=W) continue ;
        LD low = max(Ta[u]+ 3600.0 , Ta[v]+ 3600.0) ;
        LD high = min(Tb[u]-m , Tb[v]-m);
        if(low <= high && fabsl(height[u]-height[v])<=1000.0 ) 
			add_edge(u,v,low,high);
    }
}

void cal_min_arrive_time(){
    int s = ID(X ,Y) ;
    bool inq[maxn] = {0};
    memset(inq ,0 ,sizeof(inq)) ;
    queue<int>Q ;
    Q.push(s) , arrive[s] = 0.0 , inq[s] = true ;
    while(!Q.empty()){
        int u = Q.front(); Q.pop() , inq[u] = false ;   
        for(size_t i=0; i<G[u].size(); i++){
            Edge &e = edges[G[u][i]] ;
            if(arrive[u] > e.R) continue ; // 若arrive[u]<e.L 可以等一会儿,但arrive[u]>e.R不可行。 
            LD reach_time = max(arrive[u] , e.L) + m ; 
            if(reach_time < arrive[e.to]) {
                arrive[e.to] = reach_time ; 
                if(!inq[e.to]) Q.push(e.to) , inq[e.to] = true ;
            }
        }
    }
}

void cal_max_left_time(){
    int s = ID(X ,Y) ;
    bool inq[maxn] = {0};
    queue<int>Q ;
    Q.push(s) , left[s] = 12.0 *3600 , inq[s] = true ;
    while(!Q.empty()){
        int u = Q.front(); Q.pop() , inq[u] = false ;
        for(size_t i=0; i<G[u].size(); i++){
            Edge &e = edges[G[u][i]] ;
            if(left[u] < e.L) continue ;
            LD reach_time = min(left[u] , e.R) - m ;
            if(reach_time > left[e.to]) {
                left[e.to] = reach_time ;
                if(!inq[e.to]) Q.push(e.to) , inq[e.to] = true ;
            }
        }
    }
}

LD cal_best(){
    LD ret = (LD)0;
    for(int x=0; x<H ;x++) for(int y=0; y<W; y++) {
        int t = ID(x,y);
        if(arrive[t] <= left[t]) ret = max(ret , (LD)0.0+(x-X)*(x-X) + (y-Y)*(y-Y)) ;
    }
    return ret ;
}

int main()
{  
//	freopen("e:in.txt","r",stdin) ;
    scanf("%Lf%Lf", &a, &m) ;  a*=1000 ;
    scanf("%d%d%d%d" ,&W ,&H ,&Y ,&X) ; 
    for(int i=0; i<H; i++)
        for(int j=0; j<W ; j++)
            scanf("%Lf" ,&height[ID(i,j)]);
    build_graph() ; 
    cal_min_arrive_time();
    cal_max_left_time() ;
    LD ans = cal_best() ;
     
    printf("%.8Lf\n" , 10.0 * sqrtl(ans));
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值