[半平面交][最短路]JZOJ 3297 SDOI2013逃考

Posted mastervan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[半平面交][最短路]JZOJ 3297 SDOI2013逃考相关的知识,希望对你有一定的参考价值。

Description

高考又来了,对于不认真读书的来讲真不是个好消息。为了小杨能在家里认真读书,他的亲戚决定驻扎在他的家里监督他学习,有爷爷奶奶、外公外婆、大舅、大嫂、阿姨……
小杨实在是忍无可忍了,这种生活跟监狱有什么区别!为了他亲爱的小红,为了他的dota,他决定越狱!
假设小杨的家是个n*m 的矩阵,左下角坐标为(0,0),右上角坐标为(x1,y1)。小杨有n 个亲戚,驻扎在矩阵里(位置不同,且不在矩阵的边上)。小杨家里的每个地方都被亲戚监控着,而且只被距离最近的亲戚监控:
也就是说假设小杨所在的位置是(3,3),亲戚A 在(3,0),A 距离小杨距离是3;亲戚B 在(6,7),则B 距离小杨距离是5。距离A < 距离B,所以(3,3)位置由A 监控。
如果“最近距离”出现同时有几个亲戚,那么那个位置同时被那几个亲戚监控。
给出小杨的坐标(x0,y0)。因为被发现的人数越少,越狱成功的机会越大,所以小杨需要你设计一条越狱路线到达矩形的边上,且被发现的人数最少。
Ps:小杨做的方向是任意的,也就是说路线上的任意位置只需要是实数。
保证一开始小杨只被一个亲戚监控着。
 

Input

第一行,一个正整数t<=3,表示数据个数。
接下来t 个数据:
    第一行n,表示小杨的亲戚个数。
    接下来一行四个正整数,表示矩形右上角的坐标(x1,y1)和小杨的坐标(x0,y0)。
    接下来n 行,每行两个正整数,代表一个亲戚的位置。

Output

每个数据输出一个正整数,表示小杨越狱被发现人数的最小值。
 

Sample Input

3
4
10 10 5 5
5 6
3 5
7 5
5 3
0
10 10 5 5
3
3 3 2 2
1 1
2 2
2 1

Sample Output

1
0
1
 

Data Constraint

前50%数据,n<=200;
其余数据n<=600。
 

Hint

样例解释:
第一个数据,小杨直接往上走,只被(5,6)监控过。
第二个数据,小杨被(7,7)监控,走到(9,9)被(7,11)监控,然后直接往上走。

分析

我们可以发现两个亲戚之间的监事距离分界线就是他们的连线的中垂线

那么一个亲戚的监视范围就是他与其他亲戚的连线的中垂线围成的凸多边形

这就是半平面交啦

那么我们轻松3小时+地打完半平面交模板以后,我们可以发现题目要求的就是小杨经过的路线穿过半平面交的最少次数,则使半平面交相邻的亲戚连边权为1的边,跑最短路即可

 

技术图片
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <memory.h>
using namespace std;
typedef double ld;
const int N=6e2+10;
const ld eps=1e-12;

int dcmp(ld a) return a<-eps?-1:(a>eps?1:0);

struct Point 
    ld x,y;
s,t,a[N];
struct Line 
    Point p,v;
    ld angle;
    int id;
    friend bool operator < (Line a,Line b) return dcmp(a.angle-b.angle)<0;
l[N];
int lcnt;
struct Graph 
    int v,nx;
g[4*N*N];
int cnt,list[N],dis[N];
bool vis[N];
int T,n;

Point operator + (Point a,Point b) return (Point)a.x+b.x,a.y+b.y;
Point operator - (Point a,Point b) return (Point)a.x-b.x,a.y-b.y;
Point operator * (Point a,ld b) return (Point)a.x*b,a.y*b;
Point operator / (Point a,ld b) return (Point)a.x/b,a.y/b;

ld Cross_Multi(Point a,Point b) return a.x*b.y-a.y*b.x;
ld Dot_Multi(Point a,Point b) return a.x*b.x+a.y*b.y;
Point Mid_Point(Point a,Point b) return (a+b)/2.0;
Point Rotate(Point v) return (Point)-v.y,v.x;
bool Left(Line l,Point b) return Cross_Multi(l.v,b-l.p)>0;

void Add(int u,int v) 
    g[++cnt]=(Graph)v,list[u];list[u]=cnt;
    g[++cnt]=(Graph)u,list[v];list[v]=cnt;


void Pre_Process(int k) 
    lcnt=0;
    l[++lcnt]=(Line)(Point)0,t.y,(Point)0,-1,atan2(-1,0),n+1;
    l[++lcnt]=(Line)(Point)0,0,(Point)1,0,atan2(0,1),n+1;
    l[++lcnt]=(Line)(Point)t.x,0,(Point)0,1,atan2(1,0),n+1;
    l[++lcnt]=(Line)t,(Point)-1,0,atan2(0,-1),n+1;
    for (int i=1;i<=n;i++)
        if (i!=k) 
            Point mid=Mid_Point(a[i],a[k]),rt=Rotate(a[i]-a[k]);
            l[++lcnt]=(Line)mid,rt,atan2(rt.y,rt.x),i;
        
    int x=lcnt;
    sort(l+1,l+lcnt+1);


Point Linecut(Line a,Line b) 
    Point w=a.p-b.p;
    ld t=Cross_Multi(b.v,w)/Cross_Multi(a.v,b.v);
    return a.p+a.v*t;


void Halfcut(int x) 
    deque<Line> q;
    deque<Point> p;
    while (!q.empty()) q.pop_front();
    while (!p.empty()) p.pop_front();
    q.push_front(l[1]);
    for (int i=2;i<=lcnt;i++) 
        while (!p.empty()&&!Left(l[i],p.back())) q.pop_back(),p.pop_back();
        while (!p.empty()&&!Left(l[i],p.front())) q.pop_front(),p.pop_front();
        if (dcmp(Cross_Multi(l[i].v,q.back().v))==0) 
            if (Left(q.back(),l[i].p)) 
                q.pop_back();
                if (!q.empty()) 
                    p.pop_back();
                    p.push_back(Linecut(q.back(),l[i]));
                
                q.push_back(l[i]);
            
        
        else p.push_back(Linecut(q.back(),l[i])),q.push_back(l[i]);
    
    while (!p.empty()&&!Left(q.front(),p.back())) q.pop_back(),p.pop_back();
    if (p.empty()) return;
    while (!q.empty()) 
        Add(x,q.front().id);
        q.pop_front();
    


void BFS(int v0) 
    queue<int> q;
    memset(dis,0x3f,sizeof dis);memset(vis,0,sizeof vis);
    while (!q.empty()) q.pop();
    q.push(v0);dis[v0]=0;vis[v0]=1;
    while (!q.empty()) 
        int u=q.front();q.pop();
        for (int i=list[u];i;i=g[i].nx)
            if (dis[g[i].v]>dis[u]+1) 
                dis[g[i].v]=dis[u]+1;
                if (!vis[g[i].v]) q.push(g[i].v);
                vis[g[i].v]=1;
            
        vis[u]=0;
    
    printf("%d\n",dis[n+1]);


int main() 
    for (scanf("%d",&T);T;T--) 
        scanf("%d",&n);
        scanf("%lf%lf%lf%lf",&t.x,&t.y,&s.x,&s.y);
        if (!n) 
            printf("0\n");
            continue;
        
        int mx=2147483647,st;
        for (int i=1;i<=n;i++) 
            scanf("%lf%lf",&a[i].x,&a[i].y);
            if (mx>Dot_Multi(a[i]-s,a[i]-s)) 
                mx=Dot_Multi(a[i]-s,a[i]-s);
                st=i;
            
        
        cnt=0;memset(list,0,sizeof list);
        for (int i=1;i<=n;i++) 
            Pre_Process(i);
            Halfcut(i);
        
        BFS(st);
    
View Code

 

以上是关于[半平面交][最短路]JZOJ 3297 SDOI2013逃考的主要内容,如果未能解决你的问题,请参考以下文章

P3297 [SDOI2013]逃考

bzoj3199 [Sdoi2013]escape

POJ 3384 放地毯半平面交

jzoj3510NOIP2013模拟11.5B组DAY 1 (7.12)DP最短路径(path)

半平面交

笔记篇最良心的计算几何学习笔记