ZOJ3376 Safest Points [几何(geometry), 宽度优先搜索(bfs)]

题目大意:在一个w*h的格点区域内,有一些直线,定义一个点是危险点,如果它到某条直线的哈顿距离小于1;定义一个点是最安全点,如果它不是危险点,且到所有危险点的最小距离是最大的。如果所有点都是危险点,输出”MISS!”(STG中挂了一次称为1 miss),否则按字典序输出所有最安全点。

分析:首先得把危险点求出来,一个点到一条之间的曼哈顿最近点要么是它水平射线的交点(当直线斜率绝对值大于1时),要么是它垂直射线的交点(当直线斜率绝对值小于1时),证明比较显然。然后对于1000条直线,每条直线都可以用O(1000)的复杂度求出其“附近”的危险点,方法有很多种,但O(1000^2)的方法应该是会TLE的。有了这些危险点后,从这些点出发做一次bfs,求出所有点到危险点的距离,复杂度为O(1000^2)。如果最大的距离等于0,那么”MISS!”,否则就输出所有距离最大的点。

AC代码:

Accepted   3376   C++   530   10112
代码
#include<iostream>
#include
<queue>
#include
<cassert>
using namespace std;
int d[1024][1024];
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
int floor(int a,int b)
{
if(a<0) return -( (-a+b-1)/b);
else return a/b;
}
int w,h;
bool rx,ry,rxy;
void mark(int x,int y)
{
if(rxy) swap(x,y);
if(ry) y=-y;
if(rx) x=-x;
if(0<=x && x<w && 0<=y && y<h)
d[x][y]
=0;
}
void gao(int w,int h,int x,int y,int dx,int dy)
{
if((rx=(dx<0))) w=-w, x=-x, dx=-dx;
if((ry=(dy<0))) h=-h, y=-y, dy=-dy;
if((rxy=(dx<dy))) swap(w,h), swap(x,y), swap(dx,dy);
int g=gcd(dx,dy);
dx
/=g; dy/=g;
for(int i=x; i<w||i<=0; i++)
{
int j=floor(y*dx+(i-x)*dy,dx);
mark(i,j);
if((i-x)%dx!=0)
mark(i,j
+1);
}
}

int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int main()
{
int n,x,y,dx,dy,i,j;
while(scanf("%d%d%d",&w,&h,&n)!=EOF)
{
assert(
3<=w && w<=1000);
assert(
3<=h && h<=1000);
memset(d,
-1,sizeof(d));
for(int k=0;k<n;k++)
{
scanf(
"%d%d%d%d",&x,&y,&dx,&dy);
assert(
0<=x&&x<w);
assert(
0<=y && y<h);
assert(dx
!=0 || dy!=0);
gao(w,h,x,y,dx,dy);
}
int maxd=0;
queue
<pair<int,int> > q;
for(i=0;i<w;i++)
for(j=0;j<h;j++)
if(d[i][j]==0)
q.push(make_pair(i,j));
while(!q.empty())
{
x
=q.front().first;
y
=q.front().second;
q.pop();
for(int k=0;k<4;k++){
dx
=x+dir[k][0];
dy
=y+dir[k][1];
if(0<=dx&&dx<w && 0<=dy&&dy<h&& d[dx][dy]==-1)
{
maxd
=d[dx][dy]=d[x][y]+1;
q.push(make_pair(dx,dy));
}
}
}
if(maxd==0)
puts(
"MISS!");
else{
bool blank=false;
for(i=0;i<w;i++)
for(j=0;j<h;j++)
if(d[i][j]==maxd)
{
if(blank) putchar(' ');
blank
=true;
printf(
"(%d, %d)",i,j);
}
puts(
"");
}
}
return 0;
}

参考博客:http://watashi.ws/blog/1476/zojmonthly1008/

心得:下午比赛时也是用广搜+计算几何做的,当时被没有把所有的距离0点都入队,而是将射线经过的距离0的先入队,广搜是先判断与某点相邻的四个单位边是否与射线集相交,如果相交距离为0,否则为他前驱点距离+1。如果距离有缩小,则入队从新搜索,时间复杂度可想而知……

    还是解题报告的方法好啊,先求出所有距离0点,统统入队,再进行宽搜,求出所有点到的射线集的最小曼哈顿距离。然后按字典序遍历距离数组,输出最安全的点,不能用快排的,不然会TLE的。

1.assert函数

2.swap函数

3.queue<pair<int,int> >的使用。

4.解题报告中floor函数的意义,表示不解,望大牛指点……

5.解题报告中求出所有距离0点的处理技巧,保证方向向量dx dy大于0,且dx >dy,否则进行交换。然后mark的时候要根据交换状态进行还原。

posted @ 2010-08-23 21:47  孟起  阅读(300)  评论(0编辑  收藏  举报