/*
从没做过这么复杂的计算几何题,做得我好几次想quit,最后还是坚持写了下来,前前后后花了一天多时间
其实思想还是比较简单的,就是求出所有可以被信号覆盖的点,然后对这些点按照给定的源点和终点进行BFS.
所以难点就在于怎么判断哪些点可以被信号覆盖,主要有以下两点考虑
(1)天线可以覆盖和它在同一行,同一列的点
(2)对于(1)中未被覆盖的点需要进行额外判断,判断从这点抬头是否可以看到任意一根天线,如果可以即被覆盖
第二步的代码书写是最为复杂,因为需要判断当前点与天线的连线与哪些block相交,且是否有相交的block阻挡了
这条线,具体判断方法是:枚举这两点之间的所有block,先判断当前block与连线在平面上是否相交,如果相交,
求出连线与block四条边交点在连线上的高度,如果这个高度小于当前block高度,则block会阻挡信号,即这个天线
不能覆盖当前点
*/
#include <iostream>
#include <cmath>
#define maxv(a, b) ((a) >= (b) ? (a) : (b))
#define minv(a, b) ((a) <= (b) ? (a) : (b))
#define MAX_N 55
#define MAX_A 105
#define MAX_Q (MAX_N * MAX_N)
using namespace std;
//记录所有的intersection
struct node
{
int type; //1: 当前点可以被信号覆盖, 0:当前点不可以被信号覆盖
int h; //当前点右下方block的高度
bool v; //用于bfs是标识当前点是否被访问过
}nodes[MAX_N][MAX_N];
//记录位置的结构
struct pos
{
int r, c, h; //当前位置的行列号,如果这个位置用来表示block或者天线,则还有一个高度信息
};
int R, C, caseN; //输入图的行列数以及用例个数
int atnum; //天线个数
pos ats[MAX_A]; //记录天线的信息
pos sp, ep; //起点和终点
//bfs时队列结构
struct elem
{
int r, c;
int step;
};
//bfs队列
elem bfsq[MAX_Q + 1];
int head, tail;
//可以走的四个方向
int dir[4][2] = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
//判断当前格子是否为合法格子(没有越界)
bool legalStep(int r, int c)
{
return r >= 0 && r <= R && c >= 0 && c <= C;
}
//bfs算法,从起点走信号点寻找终点
int bfs()
{
bool found = false;
head = tail = 1;
nodes[sp.r][sp.c].v = true;
bfsq[tail].r = sp.r; bfsq[tail].c = sp.c; bfsq[tail].step = 0;
tail = tail % MAX_Q + 1;
while(head != tail)
{
int curR = bfsq[head].r, curC = bfsq[head].c, curStep = bfsq[head].step;
head = head % MAX_Q + 1;
//找到终点
if(curR == ep.r && curC == ep.c)
{
found = true;
return curStep * 10;
}
int newR, newC, newStep;
//走四个方向
for(int d = 0; d < 4; d++)
{
newR = curR + dir[d][0]; newC = curC + dir[d][1]; newStep = curStep + 1;
//判断当前方向是否可走
if(legalStep(newR, newC) && !nodes[newR][newC].v && (nodes[newR][newC].type == 1 || (newR == ep.r && newC == ep.c)))
{
nodes[newR][newC].v = true;
bfsq[tail].r = newR;
bfsq[tail].c = newC;
bfsq[tail].step = newStep;
tail = tail % MAX_Q + 1;
}
}
}
//无法到达
if(!found) return -1;
}
//差积
int cross(const pos &p1, const pos &p2, const pos &p3)
{
return (p3.c * 10 - p1.c * 10) * (p2.r * 10 - p1.r * 10) - (p2.c * 10 - p1.c * 10) * (p3.r * 10 - p1.r * 10);
}
//判断直线[lp11, lp12]与[lp21, lp22],是否相交
bool intersect(const pos &lp11, const pos &lp12, const pos &lp21, const pos &lp22)
{
int v1 = cross(lp11, lp12, lp21);
int v2 = cross(lp11, lp12, lp22);
int v3 = cross(lp21, lp22, lp11);
int v4 = cross(lp21, lp22, lp12);
return v1 * v2 < 0 && v3 * v4 < 0;
}
//判断天线a是否可以覆盖点p
bool canReach(const pos &a, const pos &p)
{
int r, c;
pos temp1, temp2, temp3, temp4;
//直线[a, p]的表示
//直线[a, p]的效率
double av = (double)(a.r * 10 - p.r * 10) / (double)(a.c * 10 - p.c * 10);
//直线[a, p]在y轴上的截距离
double dv = a.r * 10 - av * a.c * 10;
//点a, p之间的距离
double dist1 = sqrt(double((a.r * 10 - p.r * 10) * (a.r * 10 - p.r * 10) + (a.c * 10 - p.c * 10) * (a.c * 10 - p.c * 10)));
double dist2;
double h = a.h;
//遍历点a, p之间的block
for(r = minv(a.r, p.r); r < maxv(a.r, p.r); r++)
{
for(c = minv(a.c, p.c); c < maxv(a.c, p.c); c++)
{
//取当前block的四个点
temp1.r = r; temp1.c = c;
temp2.r = r + 1; temp2.c = c + 1;
temp3.r = r + 1; temp3.c = c;
temp4.r = r; temp4.c = c + 1;
//判断直线[a, p]是否与block[temp1, temp2, temp3, temp4]相交,用到了差积
if((intersect(a, p, temp1, temp2) || intersect(a, p, temp3, temp4)))
{
//分别计算直线[a, p]与当前block四条边所在直线交点的横纵坐标,并判断这个点是否在这个block的边上,如果是则利用等比关系计算当前点在直线[a, p]上的高度,
//如果这个高度小于当前block的高度,则被阻挡
double x, y;
//block的上边
y = r * 10; x = (y * 10 - dv) / av;
if(x >= c * 10 && x <= (c + 1) * 10)
{
dist2 = sqrt(double((y - p.r * 10) * (y - p.r * 10) + (x - p.c * 10) * (x - p.c * 10)));
double newh = h * dist2 / dist1;
if(newh < nodes[r][c].h) return false;
}
//block的下边
y = (r + 1) * 10; x = (y - dv) / av;
if(x >= c * 10 && x <= (c + 1) * 10)
{
dist2 = sqrt(double((y - p.r * 10) * (y - p.r * 10) + (x - p.c * 10) * (x - p.c * 10)));
double newh = h * dist2 / dist1;
if(newh < nodes[r][c].h) return false;
}
//block的左边
x = c * 10; y = av * x + dv;
if(y >= r * 10 && y <= (r + 1) * 10)
{
dist2 = sqrt(double((y - p.r * 10) * (y - p.r * 10) + (x - p.c * 10) * (x - p.c * 10)));
double newh = h * dist2 / dist1;
if(newh < nodes[r][c].h) return false;
}
//block的右边
x = (c + 1) * 10; y = av * x + dv;
if(y >= r * 10 && y <= (r + 1) * 10)
{
dist2 = sqrt(double((y - p.r * 10) * (y - p.r * 10) + (x - p.c * 10) * (x - p.c * 10)));
double newh = h * dist2 / dist1;
if(newh < nodes[r][c].h) return false;
}
}
}
}
//可以覆盖
return true;
}
//预处理,计算所有点是否可以被覆盖
void preProcess()
{
int i, j, k;
pos temp;
//所有天线所在的横纵轴上的点都可以被覆盖
for(k = 0; k < atnum; k++)
{
for(i = 0; i <= C; i++) nodes[ats[k].r][i].type = 1;
for(j = 0; j <= R; j++) nodes[j][ats[k].c].type = 1;
}
//对于上面不和天线在一条横纵线上的点,判断从当前点是否可以看到某个天线的最高点而不被block阻挡
for(i = 0; i <= R; i++)
{
for(j = 0; j <= C; j++)
{
if(nodes[i][j].type == 1) continue;
temp.r = i; temp.c = j;
for(k = 0; k < atnum; k++)
{
if(canReach(ats[k], temp))
{
nodes[i][j].type = 1;
break;
}
}
}
}
}
int main()
{
//ofstream output("output.txt");
int i, j;
scanf("%d", &caseN);
while(caseN--)
{
scanf("%d%d", &R, &C);
memset(nodes, 0, sizeof(nodes));
for(i = 0; i < R; i++)
for(j = 0; j < C; j++)
scanf("%d", &nodes[i][j].h);
scanf("%d%d%d%d", &sp.r, &sp.c, &ep.r, &ep.c);
scanf("%d", &atnum);
for(i = 0; i < atnum; i++) scanf("%d%d%d", &ats[i].r, &ats[i].c, &ats[i].h);
preProcess();
cout<<bfs()<<endl;
}
return 0;
}
分享到:
相关推荐
poj 2903 Joy of Mobile Routing.md
poj 2771 Guardian of Decency.md
poj 3174 Alignment of the Planets.md
### POJ 3083 Children of the Candy Corn 解题报告 #### Description 题目描述了一个典型的迷宫问题:在一个玉米地迷宫中,游客需要找到从入口到出口的路径。题目给出了一个常见的策略:选择左墙或右墙并沿着它...
标题“POJ2151-Check the difficulty of problems”是指一个编程竞赛题目,来源于北京大学的在线判题系统POJ(PKU Online Judge)。这个题目要求参赛者编写程序来评估问题的难度。描述中的“解题报告+AC代码”表明...
《POJ2109-Power of Cryptography:解题报告与AC代码解析》 在计算机科学领域,算法和编程竞赛是检验和提升编程技能的重要途径。POJ(Problem Set of Peking University)是由北京大学主办的一个在线编程竞赛平台,...
POJ2942-Knights of the Round Table 【Tarjan算法】 解题报告+AC代码 http://hi.csdn.net/!s/F3L8HO ================================== 我的POJ所有解题报告:...
【描述】"POJ1048,加强版的约瑟夫问题 难度中等" 提示我们,这个问题是编程竞赛POJ(Problemset of JiaoShi University)中的一个挑战,难度适中。约瑟夫问题是经典的计算机科学问题,它源自一个古代犹太人的传说。...
标题“POJ3253-POJ3253-Fence Repair【STL优先队列】”指的是一个在线编程竞赛题目,源自北京大学的在线判题系统POJ(Problem Online Judge)。该题目要求参赛者使用C++编程语言解决特定的问题,并且在解决方案中...
* 图的深度优先遍历和广度优先遍历:图的深度优先遍历和广度优先遍历是指遍历图的两种方式,如 poj1860、poj3259、poj1062、poj2253、poj1125、poj2240。 * 最短路径算法:最短路径算法是指计算图中两点之间的最短...
【描述】该题目来源于北京大学的在线编程平台POJ(Problem Online Judge),编号为2739,名为"Sum of Consecutive Prime Numbers"。这是一道关于算法与数论的编程题目,要求参赛者编写程序来计算连续的素数之和。...
【标题】"POJ1010-STAMPS"是一个编程题目,来源于北京大学的在线判题系统POJ(Problem Set of Peking University),这是一处训练程序员算法技能和编程能力的平台。该题目旨在考察参赛者对动态规划或贪心算法的理解...
【标题】"POJ1159-Palindrome" 是北京大学在线编程平台POJ上的一道编程题目。这道题目主要考察的是字符串处理和回文判断的知识点。 【描述】"北大POJ1159-Palindrome 解题报告+AC代码" 暗示了解决这道问题的方法和...
1. **状态转移方程**:设计复杂的动态规划状态转移方程(poj1191, poj1054, poj3280, poj2029, poj2948, poj1925, poj3034)。 2. **记忆化搜索**:结合动态规划和递归搜索(POJ3254, poj2411, poj1185)。 3. **...
根据给定的文件信息,我们可以总结出一份详细的IT知识训练计划,主要针对编程竞赛和算法学习,特别是聚焦于POJ(Problem Online Judge)平台上的题目训练。这份计划分为两个阶段,初级阶段和中级阶段,共计涉及了165...
* 较为复杂的动态规划:例如 poj1191、poj1054、poj3280、poj2029、poj2948、poj1925、poj3034。 数学 1. 组合数学: * 加法原理和乘法原理。 * 排列组合。 * 递推关系:例如 poj3252、poj1850、poj1019、poj...
- **例题**:poj1860, poj3259, poj1062, poj2253, poj1125, poj2240 - **解释**:最短路径算法包括Dijkstra算法、Bellman-Ford算法、Floyd算法以及堆优化的Dijkstra算法等。 ##### (3) 最小生成树算法 - **例题**...
【标题】"POJ2002-Squares"是一个经典的计算机编程题目,源自北京大学的在线判题系统(POJ,即PKU Online Judge)。这个题目主要涉及到算法设计和实现,尤其是数学和动态规划方面的知识。 【描述】"解题报告+AC代码...
标题中的"jihe.rar_2289_POJ 3714_poj3714_poj3714 Ra_visual c" 提到了一个压缩文件,可能包含有关编程竞赛或算法解决的资源,特别是与POJ(Problem On Judge)平台上的问题3714相关的。"Ra_visual c"可能指的是...
标题和描述中的“poj各种分类”主要指向的是在POJ(Peking University Online Judge)平台上,根据解题策略和算法类型对题目进行的分类。POJ作为一个知名的在线编程平台,提供了大量的算法练习题,适合从初学者到...