http://acm.pku.edu.cn/JudgeOnline/problem?id=1556
挺有意思的一个题。
题目大意如上图,有一个房间,要从(0,5)走到(10,5),房间内有一些竖直的墙壁,现在要求最短的路径长度。
首先,很直观很显然的,要使路径达到最短,我们每次都是沿着墙的端点按直线走,也就是从一个墙的端点走到另一个墙的端点,这样问题就可以转换为图论中的最短路问题了,设上图中有n个端点,这样可以建立一个n+2个点的有向带权图g,g[i][j]表示从端点i到端点j的长度。
下面是关于怎么构图:枚举2个端点i和j,如果线段Pi->Pj于横坐标处于Xi,Xj之间的墙壁都没有相交的话,则可以从端点i直线走到端点j,在图g中假如一条由i指向j的边,权值为distance(Pi,Pj)。
这样,最构造出来的图求最短路就可以得到最后问题的解了。
#include <iostream>
#include <string>
#include <cstdio>
#include <cmath>
using namespace std;
#define EPS 1.0e-6
struct Point {
double x,y;
};
int dblcmp(double r) {
if(fabs(r)<EPS) return 0;
return r>0?1:-1;
}
double dot(double x1,double y1,double x2,double y2) {
return x1*y2-x2*y1;
}
double cross(Point a,Point b,Point c) {
return dot(b.x-a.x,b.y-a.y,c.x-a.x,c.y-a.y);
}
//判断线段(a,b)和(c,d)是否相交(不算端点)
bool lineseg(Point a,Point b,Point c,Point d) {
return (((dblcmp(cross(a,b,c))^dblcmp(cross(a,b,d)))==-2)
&&((dblcmp(cross(c,d,a))^dblcmp(cross(c,d,b)))==-2));
}
double mydist(Point a,Point b) {
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double g[200][200]; //图的临街矩阵
int s,t;
int n;
Point line[200][2]; //存储墙
Point pts[200]; //存储墙2端的端点
int main() {
double x,y1,y2,y3,y4;
while(cin>>n) {
if(n==-1) break;
s=0,t=4*n+1;
pts[0].x=0;
pts[0].y=5;
pts[t].x=10;
pts[t].y=5;
for(int i=0;i<n;i++) {
cin>>x>>y1>>y2>>y3>>y4;
line[i*3][0].x=x;
line[i*3][0].y=0;
line[i*3][1].x=x;
line[i*3][1].y=y1;
line[i*3+1][0].x=x;
line[i*3+1][0].y=y2;
line[i*3+1][1].x=x;
line[i*3+1][1].y=y3;
line[i*3+2][0].x=x;
line[i*3+2][0].y=y4;
line[i*3+2][1].x=x;
line[i*3+2][1].y=10;
pts[4*i+1].x=x;
pts[4*i+1].y=y1;
pts[4*i+2].x=x;
pts[4*i+2].y=y2;
pts[4*i+3].x=x;
pts[4*i+3].y=y3;
pts[4*i+4].x=x;
pts[4*i+4].y=y4;
}
for(int i=s;i<=t;i++)
for(int j=s;j<=t;j++)
g[i][j]=1.0e10;
//下面建图
for(int i=0;i<=t;i++) {
for(int j=i+1;j<=t;j++) {
bool ok=true;
for(int k=0;k<3*n;k++) {
if(pts[i].x!=pts[j].x&&pts[i].x!=line[k][0].x&&pts[j].x!=line[k][0].x&&lineseg(pts[i],pts[j],line[k][0],line[k][1])) {
ok=false;
break;
}
}
if(ok) {
g[i][j]=mydist(pts[i],pts[j]);
}
}
}
//下面用Dijkstra算法求最短路
double dist[200];
for(int i=s;i<=t;i++) dist[i]=1.0e10;
dist[0]=0;
bool vis[200];
memset(vis,false,sizeof(vis));
for(int i=s;i<=t;i++) {
double mind=1.0e10;
int id;
for(int j=s;j<=t;j++) {
if(!vis[j]&&mind>dist[j]) {
id=j;
mind=dist[j];
}
}
if(mind==1.0e10) break;
vis[id]=true;
for(int j=s;j<=t;j++) {
if(!vis[j]&&dist[id]+g[id][j]<dist[j]) {
dist[j]=dist[id]+g[id][j];
}
}
}
printf("%.2f\n",dist[t]);
}
return 0;
}
#include <string>
#include <cstdio>
#include <cmath>
using namespace std;
#define EPS 1.0e-6
struct Point {
double x,y;
};
int dblcmp(double r) {
if(fabs(r)<EPS) return 0;
return r>0?1:-1;
}
double dot(double x1,double y1,double x2,double y2) {
return x1*y2-x2*y1;
}
double cross(Point a,Point b,Point c) {
return dot(b.x-a.x,b.y-a.y,c.x-a.x,c.y-a.y);
}
//判断线段(a,b)和(c,d)是否相交(不算端点)
bool lineseg(Point a,Point b,Point c,Point d) {
return (((dblcmp(cross(a,b,c))^dblcmp(cross(a,b,d)))==-2)
&&((dblcmp(cross(c,d,a))^dblcmp(cross(c,d,b)))==-2));
}
double mydist(Point a,Point b) {
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double g[200][200]; //图的临街矩阵
int s,t;
int n;
Point line[200][2]; //存储墙
Point pts[200]; //存储墙2端的端点
int main() {
double x,y1,y2,y3,y4;
while(cin>>n) {
if(n==-1) break;
s=0,t=4*n+1;
pts[0].x=0;
pts[0].y=5;
pts[t].x=10;
pts[t].y=5;
for(int i=0;i<n;i++) {
cin>>x>>y1>>y2>>y3>>y4;
line[i*3][0].x=x;
line[i*3][0].y=0;
line[i*3][1].x=x;
line[i*3][1].y=y1;
line[i*3+1][0].x=x;
line[i*3+1][0].y=y2;
line[i*3+1][1].x=x;
line[i*3+1][1].y=y3;
line[i*3+2][0].x=x;
line[i*3+2][0].y=y4;
line[i*3+2][1].x=x;
line[i*3+2][1].y=10;
pts[4*i+1].x=x;
pts[4*i+1].y=y1;
pts[4*i+2].x=x;
pts[4*i+2].y=y2;
pts[4*i+3].x=x;
pts[4*i+3].y=y3;
pts[4*i+4].x=x;
pts[4*i+4].y=y4;
}
for(int i=s;i<=t;i++)
for(int j=s;j<=t;j++)
g[i][j]=1.0e10;
//下面建图
for(int i=0;i<=t;i++) {
for(int j=i+1;j<=t;j++) {
bool ok=true;
for(int k=0;k<3*n;k++) {
if(pts[i].x!=pts[j].x&&pts[i].x!=line[k][0].x&&pts[j].x!=line[k][0].x&&lineseg(pts[i],pts[j],line[k][0],line[k][1])) {
ok=false;
break;
}
}
if(ok) {
g[i][j]=mydist(pts[i],pts[j]);
}
}
}
//下面用Dijkstra算法求最短路
double dist[200];
for(int i=s;i<=t;i++) dist[i]=1.0e10;
dist[0]=0;
bool vis[200];
memset(vis,false,sizeof(vis));
for(int i=s;i<=t;i++) {
double mind=1.0e10;
int id;
for(int j=s;j<=t;j++) {
if(!vis[j]&&mind>dist[j]) {
id=j;
mind=dist[j];
}
}
if(mind==1.0e10) break;
vis[id]=true;
for(int j=s;j<=t;j++) {
if(!vis[j]&&dist[id]+g[id][j]<dist[j]) {
dist[j]=dist[id]+g[id][j];
}
}
}
printf("%.2f\n",dist[t]);
}
return 0;
}