[半平面交][最短路]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

3410 10 5 55 63 57 55 3010 10 5 533 3 2 21 12 22 1

Sample Output

101

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);
    }
}

原文地址:https://www.cnblogs.com/mastervan/p/11107829.html

时间: 2024-10-06 00:22:10

[半平面交][最短路]JZOJ 3297 【SDOI2013】逃考的相关文章

Luogu3297 SDOI2013逃考(半平面交+最短路)

把每个人的监视范围看成点,相邻的两个监视范围连边,那么跑一遍最短路就可以了(事实上边权都为1可以直接bfs).显然最优的话不会有某个时刻同时被多人监视,要跨过去的话完全可以经过分界线而不是交点. 现在问题是怎么求出哪些监视范围相邻.考虑对于某个人的监视范围求出所有与它相邻的.两个监视范围的公共边是这两个人连线的中垂线,把这些线画出来可以发现求个半平面交就好了.注意线要求在矩形范围内.如果直线在半平面交中只剩下一个点应该去掉. #include<iostream> #include<cst

P3297 [SDOI2013]逃考

题意 两个亲戚间的范围的分界线必定为两者连线的中垂线,因此我们用半平面交\(O(n^2\log n)\)求出每个人的范围,之后相邻的两个范围连边跑最短路即可. 注意特判\(n=0\)的情况. code: #include<bits/stdc++.h> using namespace std; const int maxn=610; const double eps=1e-8; const double inf=1e12; const double Pi=acos(-1.0); int T,n,

LA 2218 (半平面交) Triathlon

题意: 有n个选手,铁人三项有连续的三段,对于每段场地选手i分别以vi, ui 和 wi匀速通过. 对于每个选手,问能否通过调整每种赛道的长度使得他成为冠军(不能并列). 分析: 粗一看,这不像一道计算几何的题目. 假设赛道总长度是1,第一段长x,第二段长y,第三段则是1-x-y 那么可以计算出每个选手完成比赛的时间Ti 对于选手i,若要成为冠军则有Ti < Tj (i ≠ j) 于是就有n-1个不等式,每个不等式都代表一个半平面. 在加上x>0, y>0, 1-x-y>0 这三个

POJ3525-Most Distant Point from the Sea(二分+半平面交)

Most Distant Point from the Sea Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 3955   Accepted: 1847   Special Judge Description The main land of Japan called Honshu is an island surrounded by the sea. In such an island, it is natural t

bzoj 1007 水平可见直线 半平面交稀里糊涂的过了...

题意:按y=Ax+B的形式给出n(<=50000)条直线,求从y值为无穷大的地方向下看能看到的直线编号 一看到题目就想到半平面交,以每条直线的上方为一个半平面,求半平面的交,交集中存在的直线就是能看到的直线 但是写出来之后发现样例都过不了... 对于样例,如果允许半平面在边界处重叠那么答案是1,2,3,如果不允许只有1.. 然后抱着试一试的心理交上去了,结果竟然直接AC了.. 后来看题解只需要考虑交点x坐标.. bzoj 1007 水平可见直线 半平面交稀里糊涂的过了...,布布扣,bubuko

POJ 2540 半平面交求可行区域面积

Hotter Colder Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 2343   Accepted: 981 Description The children's game Hotter Colder is played as follows. Player A leaves the room while player B hides an object somewhere in the room. Player

poj 3525 Most Distant Point from the Sea 半平面交 + 二分

题目来源: http://poj.org/problem?id=3525 分析: 题意:给定一个凸多边形,求多边形中距离边界最远的点到边界的距离. 思路 : 每次将凸多边形每条边往里平移d,判断是否存在核:二分d即可. 多边形边上的点(x , y)往里平移d 后的 坐标: s , e  为向量的 起点和终点, len 为起点和终点的距离, h 为平移的距离 x' = x + dx y' = y + dy dx = ( s.y - e.y ) / len * h ,( 原理 是 利用 三角形的相似

UVALive 4992 Jungle Outpost(半平面交)

题意:给你n个塔(点)形成一个顺时针的凸包,敌人可以摧毁任何塔,摧毁后剩下的塔再组成凸包 在开始的凸包内选一点为主塔,保证敌人摧毁尽量多塔时主塔都还在现在的凸包内,求出最多摧毁的塔 题解:这题关键就是选的主塔在不同的地方,敌人就会摧毁不同的塔来让你的主塔暴露 因此这样想,找出敌人摧毁不同的塔后形成的所有不同的凸包,再求出所有凸包的交就好 具体就是,首先枚举摧毁塔的个数k,再把摧毁任意k个塔所形成的所有不同的凸包求一个交,如果为空就代表了摧毁k个塔一定可以保证无论主塔在哪儿都可以暴露(关键) 而所

poj 1279 半平面交核面积

Art Gallery Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 6668   Accepted: 2725 Description The art galleries of the new and very futuristic building of the Center for Balkan Cooperation have the form of polygons (not necessarily conve