zoukankan      html  css  js  c++  java
  • P3297 [SDOI2013]逃考

    题目描述

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

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

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

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

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

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

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

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

    输入格式

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

    接下来t个数据:

    第一行n表示亲戚个数

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

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

    输出格式

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

    输入输出样例

    输入 #1

    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

    1
    2
    

    说明/提示

    数据解释:

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

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

    数据规模:

    前 50%数据. n<=200;

    其余数据 n<=600.

    求出每个亲戚与其他亲戚的垂直平分线 , 这些垂直平分线以及矩形的四周 , 构成了半平面交的凸包

    将每个半平面交求出 , 然后将他与他相邻的亲戚连边 , 边权为 1 , 跑最短路,

    这题就是重在建图,也就是求出每个亲戚的管辖范围

    注意

    1.有一些在矩形外的亲戚

    2.特判一下 (n = 0) 的情况

    3.求交点的函数 , 一定要想清楚 , 实在不行就记住 ,

    inline point meetline(Line A , Line B)
    {
    	if(dcmp(cross(A.v , B.v)) == 0) return (point){0 , 0};
    	point k = A.p - B.p;
    	return A.p + A.v * (cross(B.v , k) / cross(A.v , B.v));
    }
    

    前者减去后者

    返回前者乘上比率 , 比率是后者 * k 与总面积的比值

    4.onright 函数

    传入cross 的是直线的方向而不是直线的端点

    return dcmp(cross(A - B.p , B.v))// AC 
    return dcmp(cross(A - B.p , B.p))// WA 
    

    5.注意判断条件 是用来 (if) 还是 (continue)

    if(no[i] || j == i) countinue;
    if(!no[i] && j != i) { do .... }
    
    6.多测清空

    注意 dcmp 的传入参数的类型 一定一定是 double

    总之 , 注意细节!!

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<algorithm>
    using namespace std;
    const int N = 1000;
    const double eps = 1e-8;
    inline int read()
    {
    	register int x = 0; register char c = getchar();
    	while(c < '0' || c > '9') c = getchar();
    	while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0' , c = getchar();
    	return x;
    }
    int n , X0 , Y0 , X1 , Y1 , s , tot , cnt;
    int head[N] , no[N];
    struct edge{int v , nex;} e[N*N];
    inline int dcmp(double x) { return fabs(x) < eps ? 0 : (x < 0 ? -1 : 1); }
    void add(int u , int v) 
    { 
    	e[++cnt].v = v; e[cnt].nex = head[u]; head[u] = cnt;
    	e[++cnt].v = u; e[cnt].nex = head[v]; head[v] = cnt;
    	return ;
    }
    struct point{
    	double x , y;
    	point(double x = 0 , double y = 0) : x(x) , y(y) {}
    	point operator + (const point &A) const {return point(x + A.x , y + A.y);}
    	point operator - (const point &A) const {return point(x - A.x , y - A.y);}
    	point operator * (const double &K) const { return point(x * K , y * K); }
    	double operator * (const point &A) const {return x * A.x + y * A.y; }
    }a[N] , p[N];
    
    struct Line{
    	point p , v; int id; double ang;
    	Line(point p = point() , point v = point() , int id = 0): p(p) , v(v), id(id) { ang = atan2(v.y , v.x); }
    	bool operator < (const Line &A) const { return ang < A.ang; }
    }l[N] , sta[N];
    
    double cross(point A , point B) { return A.x * B.y - A.y * B.x; }
    double dot(point A) { return A.x * A.x + A.y * A.y; }
    double dis(point A , point B) { return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));}
    inline bool onright(point A , Line B) { return dcmp(cross(A - B.p , B.v)) > 0; } // !!!!
    inline point meetline(Line A , Line B)
    {
    	if(dcmp(cross(A.v , B.v)) == 0) return (point){0 , 0};
    	point k = A.p - B.p;
    	return A.p + A.v * (cross(B.v , k) / cross(A.v , B.v));
    }
    
    point rot(point A) { return point(-A.y , A.x); }
    Line get_mid_line(point A , point B , int i) // A 和 B 的垂直平分线
    {
    	point p = point((A.x + B.x) / 2 , (A.y + B.y) / 2);
    	return Line(p , rot(B - A) , i);
    }
    
    void init(int id)
    {
    	tot = 0;
    	l[++tot] = Line(point(0 , 0) , point(1 , 0) , n + 1);
    	l[++tot] = Line(point(X1 , 0) , point(0 , 1) , n + 1);
    	l[++tot] = Line(point(X1 , Y1) , point(-1 , 0) , n + 1);
    	l[++tot] = Line(point(0 , Y1) , point(0 , -1) , n + 1);
    	for(int i = 1 ; i <= n ; ++i) if(i != id && !no[i]) // !!!
    		l[++tot] = get_mid_line(a[id] , a[i] , i);
    	return ;
    }
    
    void calc(int id)
    {
    	int n = tot , tail , head;
    	sort(l + 1 , l + 1 + n); sta[head = tail = 1] = l[1];
    	for(int i = 2 ; i <= n ; ++i)
    	{
    		while(head < tail && onright(p[tail-1] , l[i])) tail--;
    		while(head < tail && onright(p[head] , l[i])) head++;
    		sta[++tail] = l[i];
    		if(dcmp(cross(sta[tail].v , sta[tail-1].v)) == 0)
    		{
    			tail--;
    			if(onright(sta[tail].p , l[i])) sta[tail] = l[i];
    		}
    		if(head < tail) p[tail-1] = meetline(sta[tail] , sta[tail-1]);
    	}
    	while(head < tail && onright(p[tail-1] , sta[head])) tail--;
    	if(head >= tail) return ;
    	p[tail] = meetline(sta[head] , sta[tail]);
    	for(int i = head ; i <= tail ; ++i) add(id , sta[i].id);
    	return ;
    }
    
    int d[N] , vis[N];
    int spfa(int s)
    {
    	queue<int> q; q.push(s);
    	for(int i = 1 ; i <= n + 1 ; ++i) d[i] = 1e9 , vis[i] = 0;
    	d[s] = 0;
    	while(q.size())
    	{
    		int x = q.front(); q.pop(); vis[x] = 0;
    		for(int i = head[x] ; i ; i = e[i].nex)
    		{
    			int v = e[i].v;
    			if(d[v] > d[x] + 1)
    			{
    				d[v] = d[x] + 1;
    				if(!vis[v]) vis[v] = 1 , q.push(v);
    			}
    		}
    	}
    	return d[n+1];
    }
    
    void clear() // 多测清空
    {
    	memset(head , 0 , sizeof head); cnt = 0;
    	memset(no , 0 , sizeof no);
    	return ;
    }
    
    int main()
    {
    	int T = read();
    	while(T --> 0)
    	{
    		n = read();
    		X1 = read(); Y1 = read(); X0 = read(); Y0 = read();
    		if(n == 0) { puts("0"); continue; }
    		double minn = 1e50 , len = 1e50;
    		for(int i = 1 , x , y ; i <= n ; ++i)
    		{
    			x = read() , y = read() , a[i] = point(1.0 * x , 1.0 * y);
    			if(x > X1 || y > Y1) no[i] = 1;
    			len = dis(a[i] , point(X0 , Y0));
    			if(len < minn) minn = len , s = i;
    		}
    		//printf("---------------------
    %d
    --------------------------
    " , s);
    		for(int i = 1 ; i <= n ; ++i)
    		{
    			if(no[i]) continue;
    			init(i); calc(i);
    		}
    		printf("%d
    " , spfa(s));
    		clear();
    	}
    	return 0;
    }
    /*
    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
     */
    
  • 相关阅读:
    Linux编程 22 shell编程(输出和输入重定向,管道,数学运算命令,退出脚本状态码)
    mysql 开发进阶篇系列 46 物理备份与恢复( xtrabackup的 选项说明,增加备份用户,完全备份案例)
    mysql 开发进阶篇系列 45 物理备份与恢复(xtrabackup 安装,用户权限,配置)
    mysql 开发进阶篇系列 44 物理备份与恢复( 热备份xtrabackup 工具介绍)
    Linux编程 21 shell编程(环境变量,用户变量,命令替换)
    Linux编程 20 shell编程(shell脚本创建,echo显示信息)
    mysql 开发进阶篇系列 43 逻辑备份与恢复(mysqldump 的基于时间和位置的不完全恢复)
    Linux编程 19 编辑器(vim 用法)
    (网页)angularjs中的interval定时执行功能(转)
    (网页)在SQL Server中为什么不建议使用Not In子查询(转)
  • 原文地址:https://www.cnblogs.com/R-Q-R-Q/p/12149359.html
Copyright © 2011-2022 走看看