可以发现,最短路一定要经过墙壁的断点。
那么把房间看作一个有向图,墙壁的断点为节点,求从起点到终点的最短路。
这道题的难点在于建图。枚举所有的断点,若可以走则加入这条边。
判断两点是否连通,即为判断两点之间是否有其他墙壁阻隔。
两点的连线可以看作一个一次函数$y=kx+B$,
$k=(x2-x1)/(y2-y1),B=y1-k*x1$
得到函数解析式后,算出中间的每一个墙壁与这条直线交点的$y$坐标,
由于给出墙壁的$x$是递增的,所以只需要枚举墙壁$x1+1$~$x2-1$。
若这个$y$恰好在墙壁的缺口里,则是连通的。
边的权值即为两点之间的欧几里德距离:$sqrt( (x2-x1)^2 + (y2-y1)^2 )$
边的序号:由于一条墙壁只有四个断点,则某个断点的序号可以记作$x*4+y[i]$,$i$为第几个断点。
数据范围很小,最后用floyd求出最短路即可。
注意开double!
代码如下
#include<cstdio> #include<iostream> #include<cstring> #include<cmath> #define MogeKo qwq using namespace std; const int maxn = 30; const int INF = 2147483647; int n; double e[200][200]; struct wall { double x,y[5]; } w[maxn]; bool check(int a,int b,int g1,int g2) { if(b-a<2)return true; double xi = w[a].x,xii = w[b].x; double yi = w[a].y[g1],yii = w[b].y[g2]; double k = (yii-yi)/(xii-xi); double B = yi-k*xi; for(int i = a+1; i <= b-1; i++) { double yy = k*w[i].x+B; if(!((yy>w[i].y[1]&&yy<w[i].y[2])||(yy>w[i].y[3]&&yy<w[i].y[4])))return false; } return true; } void add(int a,int b,int g1,int g2) { if(!check(a,b,g1,g2))return; double xi = w[a].x,xii = w[b].x; double yi = w[a].y[g1],yii = w[b].y[g2]; e[(a<<2)+g1][(b<<2)+g2] = sqrt(pow(xii-xi,2)+pow(yii-yi,2)); } void floyd() { for(int k = 1; k <= (n<<2)+4; k++) for(int i = 1; i <= (n<<2)+4; i++) for(int j = 1; j <= (n<<2)+4; j++) e[i][j] = min(e[i][j],e[i][k]+e[k][j]); } int main() { scanf("%d",&n); for(int i = 1; i <= n; i++) { scanf("%lf",&w[i].x); for(int j = 1; j <= 4; j++) scanf("%lf",&w[i].y[j]); } w[0].x = 0,w[++n].x = 10; for(int i = 1; i <= 4; i++) w[0].y[i] = w[n].y[i] = 5; for(int i = 1; i <= (n<<2)+4; i++) for(int j = 1; j <= (n<<2)+4; j++) e[i][j] = INF; for(int i = 0; i <= n; i++) for(int j = i+1; j <= n; j++) for(int k = 1; k <= 4; k++) for(int l = 1; l <= 4; l++) add(i,j,k,l); floyd(); printf("%.2lf",e[1][(n<<2)+1]); return 0; }