C numi和弓道 双指针(2020牛客寒假基础训练营1 )

链接:https://ac.nowcoder.com/acm/contest/3002/C
来源:牛客网

题意:

umi对弓道非常痴迷。
有一天,她在研究一个射箭问题:
在一个无限大的平面中,她站在 (x0,y0) 这个坐标。
有 n个靶子,第 i 个靶子的坐标是(xi,yi)
umi准备在 x 轴或 y 轴上放置一块挡板来挡住弓箭的轨迹,使得她可以射中的靶子数量不超过k 个。
她想知道挡板的最短长度是多少?
注:假定弓箭的轨迹是起点为umi坐标、长度无穷大的射线。umi和靶子的体积可以无视。挡板的边缘碰到弓箭轨迹也可视为挡住弓箭。
注2:挡板不能弯折,起始和终点必须在同一坐标轴上。

输入描述:

第一行为两个整数x0,y0,代表umi坐标

第二行两个整数n和k,分别代表靶子的总数量、放置挡板后可射中靶子的最大值

接下来n行,每行两个整数xi,yi,代表每个靶子的坐标

保证没有任何一个点再坐标轴上,保证没有重合两点

(1<=n<=100000,0<=k<=n-2,-2*10e9<=xi,yi<=2*10e9)

输出描述:

若无论如何无法保证可以射中的靶子数量不超过k个,则输出-1.

否则输出挡板的最小值。如果你和正确答案的误差不超过10e-6,则视为答案正确。

示例1:

输入:

1 1
2 0
-1 2
-2 1

输出:

0.50000000

题解说对于cf难度分数为2000,咱没那水平也分析不出来。不过画个图之后思路就比较清晰了。

首先知道我们只能放一个挡板,不是在x轴就是在y轴,但最终剩下的点的个数肯定为k个,所以我们的挡板需要挡住n-k个点。

然而,若x轴上的点和y轴上的点都超过k(也包含对角象限的情况,即在x,y轴上都会存在挡点),判断起来就很麻烦了,博主最开始就在这里卡住。发觉这道题着实难。

于是我们可以逆着来,我们需要这个挡板挡住n-k个点,那么只要x轴上或y轴上的点数超过n-k就可以了!就不用管对角象限的情况了。这道题就变得非常简单。

先把x,y轴上的点分别存入v2,v1,再对两个轴分别遍历。怎么遍历呢,这就用到了双指针(博主也是头回碰)。设这个挡板的起始位置为i,那么末位置就应该为i+n-k-1。头指针和尾指针同时前进,求得最小值!。

#include<iostream>
#include<algorithm>
#include<vector>
#include<cstdio>
using namespace std;
int main(){
	int x0,y0,n,k;
	double x,y,res;
	vector<double> v1,v2;
	cin>>x0>>y0>>n>>k;
	k=n-k;    //逆向关键之处
	for(int i=0;i<n;i++){
		cin>>x>>y;
		if(x*x0<0) v1.push_back(y0-x0*(y0-y)/(x0-x));  //在y轴设置挡板,存入对应y值坐标 
		if(y*y0<0) v2.push_back(x0-y0*(x0-x)/(y0-y)); //在x轴设置挡板 存入对应x值坐标 
	}
	sort(v1.begin(),v1.end());    //坐标排序,依次遍历 
	sort(v2.begin(),v2.end());
	res=1e18;
	if(v1.size()>=k){
		int head=0,tail=k-1;
		while(tail<v1.size()){
				res=min(res,v1[tail]-v1[head]);
				tail++;head++;
			
		}
	}
	if(v2.size()>=k){
		int head=0,tail=k-1;
		while(tail<v2.size()){
				res=min(res,v2[tail]-v2[head]);
				tail++;head++;
			}
		
	}
	if(res==1e18) cout<<"-1"<<endl;
	else
		printf("%.7lf\n",res);
}

猜你喜欢

转载自blog.csdn.net/Pinoochio/article/details/104184994