题意:一个正方形密室边长是10,内部有平行y轴方向的n堵墙,每堵墙都有两道门。要从(0,5)走到(10,5)最短距离是多少?
分析:每道门的两个端点以及起点终点作为节点,要求起点到终点的最短路,建图时结合线段相交来判断两个顶点之间是否可达,就枚举这两个顶点之间的墙与这两个顶点相连而成的线段有没有相交,相交则不可达,不相交则为这线段的长度。Dijkstra + 线段相交判断,数据的组织要清晰。
#include<cstdio> #include<cmath> #define vector point struct point { double x,y; point(double xx = 0,double yy = 0) { x = xx; y = yy; } point operator - (point s) { return point(x - s.x, y - s.y); } }; //叉积 double cross_product(vector v1,vector v2) { return v1.x * v2.y - v1.y * v2.x; } //线段 struct segment { point end[2]; segment(point p1 = point(0,0),point p2 = point(0,0)) { end[0] = p1; end[1] = p2; } }; segment seg[18][3]; point node[18][4]; int n; bool tag[101];//tag[]是否访问过的标志 double matrix[101][101],dis[101];//dis存储当前从起点到该点的最短距离 const point sp = point(0,5),ep = point(10,5); void dijkstra(int start)//start是出发点 { int i,j,min_N; for(i=0;i<n * 4 + 2;i++) { dis[i]=matrix[start][i]; tag[i]=false; } tag[start]=true; dis[start]=1e10; for(j=0;j<n * 4 + 2;j++) { min_N=start; for(i=0;i<n * 4 + 2;i++) { if(!tag[i]) { if(dis[min_N]>dis[i]) min_N=i; } } //更新标志和距离 if(min_N!=start) { tag[min_N]=true; for(i=0;i<n * 4 + 2;i++) { if(!tag[i] && dis[i]>matrix[min_N][i]+dis[min_N]) dis[i]=matrix[min_N][i]+dis[min_N]; } } else break; } } //线段相交判断(跨立试验) bool intersect(segment seg1,segment seg2) { if(cross_product(vector(seg2.end[0] - seg1.end[0]),vector(seg1.end[1] - seg1.end[0])) * cross_product(vector(seg1.end[1] - seg1.end[0]),vector(seg2.end[1] - seg1.end[0])) <= 0) return false; if(cross_product(vector(seg1.end[0] - seg2.end[0]),vector(seg2.end[1] - seg2.end[0])) * cross_product(vector(seg2.end[1] - seg2.end[0]),vector(seg1.end[1] - seg2.end[0])) <= 0) return false; return true; } int toNum(int x,int y) { return x * 4 + y + 1; } double distance(point p1,point p2) { vector v = p1 - p2; return sqrt(v.x * v.x + v.y * v.y); } int main() { while(scanf("%d",&n) && n != -1) { for(int i = 0;i < n;i++) { double x,y; scanf("%lf",&x); for(int j = 0;j < 4;j++) { scanf("%lf",&y); node[i][j] = point(x,y); switch(j) { case 0: seg[i][0] = segment(point(x,0),node[i][j]); break; case 2: seg[i][1] = segment(node[i][j - 1],node[i][j]); break; case 3: seg[i][2] = segment(node[i][j],point(x,10)); } } } //建图 for(int i = 0;i < n * 4 + 2;i++) { for(int j = 0;j < n * 4 + 2;j++) matrix[i][j] = 1e10; matrix[i][i] = 0; } for(int i = 0;i < n;i++) { for(int j = 0;j < 4;j++) { bool flag = true; for(int k = 0;k < i;k++) { for(int m = 0;m < 3;m++) { if(intersect(segment(sp,node[i][j]),seg[k][m])) { flag = false; break; } } if(!flag) break; } if(flag) matrix[0][toNum(i,j)] = distance(sp,node[i][j]); } } bool flag = true; for(int k = 0;k < n;k++) { for(int m = 0;m < 3;m++) { if(intersect(segment(sp,ep),seg[k][m])) { flag = false; break; } } if(!flag) break; } if(flag) matrix[0][n * 4 + 1] = distance(sp,ep); for(int x = 0;x < n;x++) { for(int y = 0;y < 4;y++) { //以上是起点 for(int i = x + 1;i < n;i++) { for(int j = 0;j < 4;j++) { //以上是终点 bool flag = true; for(int k = x + 1;k < i;k++) { for(int m = 0;m < 3;m++) { if(intersect(segment(node[x][y],node[i][j]),seg[k][m])) { flag = false; break; } } if(!flag) break; } if(flag) matrix[toNum(x,y)][toNum(i,j)] = distance(node[x][y],node[i][j]); } } bool flag = true; for(int k = x + 1;k < n;k++) { for(int m = 0;m < 3;m++) { if(intersect(segment(node[x][y],ep),seg[k][m])) { flag = false; break; } } if(!flag) break; } if(flag) matrix[toNum(x,y)][n * 4 + 1] = distance(node[x][y],ep); } } dijkstra(0); printf("%.2lf\n",dis[n * 4 + 1]); } return 0; }