【HAOI2014】走出金字塔

神奇……

原题:

在探险的过程中,考古学家Dr. Kong 无意地被困在一个金字塔中。金字塔中的每个房间都是三角形。Dr. Kong可以破壁走到相邻的房间去。
::点击图片在新窗口中打开::


例如,如果他目前处于三角形(2,2)房间,那么他可以破壁走到三角形(2,1)、(2,3)或(1,1)房间。但破壁一面墙需要花费K分钟时间,而考古学家Dr. Kong 的体能只能支持他到S分钟。
好在Dr. Kong手中有这个金字塔地图,他发现金字塔有许多出口,一旦他进入一个有出口的三角形房间,他再用1分钟就可以走出金字塔。
::点击图片在新窗口中打开::
现在,你能否帮助Dr. Kong找到一个走出金字塔花费时间最少的出口?若能,输出Dr. Kong走出金字塔后还剩下的体能时间(应当大于或等于0);若不能,输出-1。

1 <= N <= 1000000   0<=M<=10000  0<K<=20  10<=S<=10000

这题第一眼,诶spfa搞一搞,但是建图点数是O(n^2)的,n<=1e6搞不了

但是拆每个墙的花费是固定的,所以是不是可以搞个奇奇怪怪的计算公式?

那就要找规律了,先画个图,一个三层的金字塔大概是酱紫的

然后画一下连边的关系,再对齐,差不多就是酱紫

画个四层的更好推规律

因为是无向边,所以把从坐上到右下和从右下到坐上放到一起考虑,左下到右上和右上到左下同理

然后可以发现,从(x,y)到(x,y+1)都要走两格,并且从坐上到右下可以顺着斜边走,使x和y都增加

如果从左下到右上就要逆着斜边上去,这个时候显然最优方案是每次话2条边上去,然后平着走

顺着斜边走的话有点麻烦,我开始的做法是max(abs(y2-y1),abs(x2-x1)*2),后来的做法是max(abs(y2-y1),abs(x2-x1)*2)-((x1&1)==(x2&1) && abs(y1-y2)&1);(考虑(1,1)到(3,4)的情况

证明就不证了,这题太玄了……

(至于我怎么想出来这个表达式的?面向数据编程一,一
果然我画的图的确没有卵用吗 _(:3 」∠)_

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 const int oo=168430090;
 8 int rd(){int z=0,mk=1;  char ch=getchar();
 9     while(ch<'0'||ch>'9'){if(ch=='-')mk=-1;  ch=getchar();}
10     while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0';  ch=getchar();}
11     return z*mk;
12 }
13 int n,m,k,s;
14 int sx,sy,tx,ty;
15 int mn=oo;
16 int cclt(int x1,int y1,int x2,int y2){
17     if(x1<x2 ^ y1<y2)  return abs(y2-y1)+abs(x2-x1)*2;
18     else  return max(abs(y2-y1),abs(x2-x1)*2)-((x1&1)==(x2&1) && abs(y1-y2)&1);
19 }
20 int main(){//freopen("ddd.in","r",stdin);
21     cin>>n>>m>>k>>s;
22     cin>>sx>>sy;
23     for(int i=1;i<=m;++i){
24         tx=rd(),ty=rd();
25         mn=min(mn,cclt(sx,sy,tx,ty));
26     }
27     mn=s-mn*k-1;
28     cout<<(mn>=0?mn:-1)<<endl;
29     return 0;
30 }
View Code
原文地址:https://www.cnblogs.com/JSL2018/p/6492098.html