一、传送门
http://poj.org/problem?id=1556
二、算法分析说明与代码编写指导
建图的方法是:
如果两个点所在墙的编号相差 1 (起点和终点的编号记为 0 和 4n +1),那么把这两点连起来(无向边);
如果两个点在同一个墙上,这两点不连边;
如果两个点中间隔了其它的墙,需要先判断是否能直接到达而不是被中间的墙挡住。判断的方法是:把这两点连一条直线(得到斜截式 y = kx + b),考察中间的墙的开口部分是否经过直线。若是,则这堵墙不会挡住这两点的直连边;否则,会挡住,这两点不可以连边。
建完图以后直接跑一次最短路就可以了。
Dijkstra 算法
wid 返回一个点所在墙的编号。1、2、3、4 所在的墙编号为 1;5、6、7、8 对应编号 2,以此类推。
三、AC 代码
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<bitset>
#include<vector>
#pragma warning(disable:4996)
using namespace std;
struct wall { double x, y[4]; }; struct edge { int v; double w; }; struct point { int id; double x, y; };
const double dm = 1e9; const int nm = 74, wm = 20;
int m, n, t, x, y; wall w[wm]; vector<edge> g[nm]; point p[nm]; edge e; bitset<nm> v; double d[nm + 1];
inline double dist(const point& p, const point& q) { return sqrt(pow(p.x - q.x, 2) + pow(p.y - q.y, 2)); }
inline int wid(const point& p) { return p.id / 4 + (p.id % 4 != 0); }
inline bool reachable(const point& p, const point& q) {
double k = (q.y - p.y) / (q.x - p.x), b = p.y - k * (p.x), y;
for (int i = wid(p) + 1, t = wid(q); i < t; ++i) {
y = k * w[i].x + b; if (y < w[i].y[0] || y > w[i].y[3] || (y > w[i].y[1] && y < w[i].y[2]))return false;
}
return true;
}
int main() {
p[0].x = 0; p[0].y = 5;
for (;;) {
scanf("%u", &n); if (n == -1)return 0;
d[0] = t = 0; v.reset(); m = 0; d[nm] = dm;
for (int i = 1; i <= n; ++i) {
scanf("%lf%lf%lf%lf%lf", &w[i].x, &w[i].y[0], &w[i].y[1], &w[i].y[2], &w[i].y[3]);
for (int j = 0; j < 4; ++j) { p[++t].x = w[i].x; p[t].y = w[i].y[j]; p[t].id = t; }
}
p[++t].x = 10; p[t].y = 5; p[t].id = t;
fill(d + 1, d + 1 + t, dm); for (int i = 0; i <= t; ++i)g[i].clear();
for (int i = 0; i <= t; ++i) {
for (int j = i + 1; j <= t; ++j) {
switch (wid(p[j]) - wid(p[i])) {
case 0:continue;
case 1:e.v = j; e.w = dist(p[i], p[j]); g[i].push_back(e); e.v = i; g[j].push_back(e); continue;
default:
if (reachable(p[i], p[j])) {
e.v = j; e.w = dist(p[i], p[j]); g[i].push_back(e); e.v = i; g[j].push_back(e);
}
}
}
}
while (m < t) {
x = nm; for (int i = 0; i <= t; ++i)if (!v[i] && d[i] < d[x]) { x = i; }
v[x] = true; ++m;
for (size_t i = 0; i < g[x].size(); ++i) { y = g[x][i].v; d[y] = min(d[y], d[x] + g[x][i].w); }
}
printf("%.2f\n", d[t]);
}
}
本文详细解析POJ 1556问题,通过建图方法连接符合条件的点,并利用Dijkstra算法求解最短路径。建图策略包括考虑点所在墙的编号差异和能否直接到达,最后给出AC代码实现。
720

被折叠的 条评论
为什么被折叠?



