[Luogu P3297] [SDOI2013] 逃考

洛谷传送门

题目描述

髙考又来了,对于不认真读书的来讲真不是个好消息为了小杨能在家里认真读书,他 的亲戚决定驻扎在他的家里监督他学习,有爷爷奶奶、外公外婆、大舅、大嫂、阿姨……

小杨实在是忍无可忍了,这种生活跟监狱有什么区别!为了他亲爱的小红,为了他的 dota,他决定越狱!

假设小杨的家是个 n×m n × m 的矩阵,左下角坐标为 (0,0) ( 0 , 0 ) ,右上角坐标为 (xl,yl) ( x l , y l ) 。小 杨有n个亲戚,驻扎在矩阵里(位置不同,且不在矩阵的边上)。小杨家里的每个地方都被 亲戚监控着,而且只被距离最近的亲戚监控:

也就是说假设小杨所在的位置是 (3,3) ( 3 , 3 ) ,亲戚A在 (3,0) ( 3 , 0 ) , A距离小杨距离是3;亲戚 B在 (6,7) ( 6 , 7 ) ,则B距离小杨距离是5。距离A<距离B,所以 (3,3) ( 3 , 3 ) 位置由A监控。

如果“最近距离”出现同时有几个亲戚,那么那个位置同时被那几个亲戚监控。

给出小杨的坐标 (x0,y0) ( x 0 , y 0 ) 。因为发现的人数越少,越狱成功的机会越大,所以小杨需要你设计一条越狱路线到达矩形的边上,且被发现的人数最少。

Ps:小杨做的方向是任意的,也就是说路线上的任意位置H需耍是实数。

保证一开始小杨只被一个亲戚监控着。

输入输出格式

输入格式:

第一行 一个正整数 t<=3 t <= 3 表示数据组数

接下来t个数据:

第一行n表示亲戚个数

第二行4个正整数表示举行右上角坐标 (x1,y1) ( x 1 , y 1 ) 和小杨的坐标 (x0,y0) ( x 0 , y 0 )

接下来n行,每行2个正整数表示一个亲戚的位置

输出格式:

每个数据一个正整数表示越狱被发现人数的最小值

输入输出样例

输入样例:

2
4
10 10 5 5
5 6
3 5
7 5
5 3
17
14 12 7 6
7 11
6 9
7 7
1 10
2 20
1 6
2 6
1 1
2 2
5 1
5 2
13 1
12 2
12 7
13 7
12 11
13 11

输出样例:

1
2

说明

数据解释:

第一个数据, 小杨直接往上走,只被 (5,6) ( 5 , 6 ) 监控过.

第二个数据,小杨被 (7,7) ( 7 , 7 ) 监控- 走到 (9,9) ( 9 , 9 ) (7,11) ( 7 , 11 ) 监控, 然后直接往上走.

数据规模:

前 50%数据. n<=200 n <= 200 ;

其余数据 n<=600 n <= 600 .

解题分析

先给大家看一张妙妙的

设矩阵范围为 ABCD ◻ A B C D , 亲戚为 E,F,G,H E , F , G , H ,显然对于每个亲戚, 都有一定的控制范围(用色块表示), 而我们肯定不会走有多个亲戚同时监控的点(矩阵内部色块夹角处)

那么我们可以 O(N2log(N)) O ( N 2 l o g ( N ) ) 预处理出每个亲戚相邻的亲戚, 再建图连边跑最短路即可。 对于每个控制范围与边界相交的亲戚,我们连一条到终点的边。 起点只需要在输入坐标时比较距离即可。

打板即可。

代码:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <cstdlib>
#include <queue>
#include <algorithm>
#define R register
#define gc getchar()
#define IN inline
#define W while
#define MX 1005
#define EPS 1e-8
#define db double
int num, st, ed, lcnt, head, tail;
db rig, up, now_x, now_y, min_dis;
namespace Geometry
{
    using std::sort;
    bool out[MX];
    struct pt
    {
        db x, y;
    }man[MX], inter[MX];
    IN pt operator + (const pt &x, const pt &y) {return (pt){x.x + y.x, x.y + y.y};}
    IN pt operator - (const pt &x, const pt &y) {return (pt){x.x - y.x, x.y - y.y};}
    IN db operator * (const pt &x, const pt &y) {return x.x * y.y - x.y * y.x;}
    IN db operator / (const pt &x, const pt &y) {return x.x * y.x + x.y * y.y;}
    IN pt operator * (const pt &x, const db &y) {return (pt){x.x * y, x.y * y};}
    IN pt operator / (const pt &x, const db &y) {return (pt){x.x / y, x.y / y};}
    IN pt get_mid (const pt &x, const pt &y) {return (pt){(x.x + y.x) / 2.0, (x.y + y.y) / 2.0};}
    IN db get_dis (const pt &x) {return sqrt(x.x * x.x + x.y * x.y);}
    IN int sig (const db &x) {return (x > -EPS) - (x < EPS);}
    IN bool operator < (const pt &x, const pt &y) {return x.x == y.x ? x.y < y.y : x.x < y.x;}
    IN pt rotate (const pt &x) {return (pt){-x.y, x.x};}
    struct line
    {
        pt a, b;
        db ang;
        int id;
        line(){}
        line(pt x, pt y, int t): a(x), b(y), id(t){ang = atan2(b.y - a.y, b.x - a.x);}
    }lin[MX], que[MX];
    IN bool operator < (const line &x, const line &y) {return x.ang < y.ang;}
     IN pt get_int(const line &x, const line &y)
    {
        db k1 = (y.b - x.a) * (x.b - x.a);
        db k2 = (x.b - x.a) * (y.a - x.a);
        return (y.b - y.a) * (k2 / (k1 + k2)) + y.a;
    }
    IN bool onleft (const line &x, const pt &y)
    {return (x.b - x.a) * (y - x.a) > EPS;}
}
namespace SPFA
{
    using std::queue;
    int hd[MX], dist[MX];
    bool inq[MX];
    queue <int> q;
    struct Edge
    {
        int to, nex;
    }edge[MX * 100];
    int cnt;
    IN void addedge(const int &from, const int &to)
    {
        edge[++cnt] = (Edge){to, hd[from]};
        hd[from] = cnt;
        edge[++cnt] = (Edge){from, hd[to]};
        hd[to] = cnt;
    }
    int spfa()
    {
        memset(dist, 63, sizeof(dist));
        dist[st] = 0;
        q.push(st);
        R int now;
        W (!q.empty())
        {
            now = q.front();
            q.pop();
            for (R int i = hd[now]; i; i = edge[i].nex)
            {
                if(dist[edge[i].to] > dist[now] + 1)
                {
                    dist[edge[i].to] = dist[now] + 1;
                    if(!inq[edge[i].to])
                    {
                        inq[edge[i].to] = false;
                        q.push(edge[i].to);
                    }
                }
            }
            inq[now] = false;
        }
        return dist[ed];
    }
}
using namespace Geometry;
using namespace SPFA;
void reset()
{
    memset(out, false, sizeof(out));
    memset(hd, 0, sizeof(hd));
    min_dis = 1e9;
}
int main(void)
{
    int T;
    scanf("%d", &T);
    W (T--)
    {
        reset();
        scanf("%d", &num);
        ed = num + 1;
        scanf("%lf%lf%lf%lf", &rig, &up, &now_x, &now_y);
        if(!num)
        {printf("0\n"); continue;}
        for (R int i = 1; i <= num; ++i)
        {
            scanf("%lf%lf", &man[i].x, &man[i].y);
            if(man[i].x > rig || man[i].x < 0 || man[i].y <0 || man[i].y > up) out[i] = true;
            db dis = get_dis(man[i] - (pt){now_x, now_y});
            if(dis < min_dis) min_dis = dis, st = i;
        }
        for (R int i = 1; i <= num; ++i)
        {
            if(out[i]) continue;
            lin[0] = line((pt){0, up}, (pt){0, -1}, ed);
            lin[1] = line((pt){0, 0},  (pt){1, 0}, ed);
            lin[2] = line((pt){rig, 0}, (pt){rig, up}, ed);
            lin[3] = line((pt){rig, up}, (pt){-1, up}, ed);
            lcnt = 3;
            for (R int j = 1; j <=num; ++j)
            {
                if(i == j || out[j]) continue;
                pt mid = get_mid(man[i], man[j]);
                pt rot = mid + rotate(man[j] - mid);
                lin[++lcnt] = line(mid, rot, j);
            }
            sort(lin, lin + lcnt + 1);
            head = tail = 0;
            que[0] = lin[0];
            for (R int j = 1; j <= lcnt; ++j)
            {
                W (head < tail && !onleft(lin[j], inter[tail - 1])) --tail;
                W (head < tail && !onleft(lin[j], inter[head])) ++head;
                que[++tail] = lin[j];
                if(!sig((que[tail - 1].b - que[tail - 1].a) * (que[tail].b - que[tail].a)))
                {
                    tail--;
                    if(onleft(que[tail], lin[j].a)) que[tail] = lin[j];
                }
                if(head < tail) inter[tail - 1] = get_int(que[tail], que[tail - 1]);
            }
            W (head < tail && !onleft(que[head], inter[tail - 1])) --tail;
            if(tail - head <= 1) continue;
            for (R int j = head; j <= tail; ++j) addedge(i, que[j].id);
        }
        printf("%d\n", spfa());
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值