首页 > 其他分享 >[题解]POJ2074 Line of Sight

[题解]POJ2074 Line of Sight

时间:2024-07-16 22:53:00浏览次数:6  
标签:题解 lin POJ2074 segs 盲区 house x2 Line x1

POJ2074 Line of Sight

题意简述

多测。给定若干条线段,全部与\(x\)轴平行。

其中有\(2\)条线段表示房子和人行道(虽然翻译不是人行道就是了),保证房子在人行道上面。

其他线段表示障碍物(不保证在房子和人行道之间)。

请找出人行道上最长的连续部分,使得在这中间可以完整地看到房子的全貌。

如果存在,输出其长度即可,保留\(2\)位小数;如果不存在输出No View

数据范围没给,不过开\(10^5\)能过。具体输入格式见题目。

思路分析

先以人行道的左端点为\((0,0)\),将所有坐标整体移动一下,方便考虑。

我们发现想直接计算能看到的区域比较困难,但是可以计算出每个障碍物形成的盲区:

如图,盲区就是房子的左右端点和障碍物的右左端点(注意顺序)连接并延长,与人行道相交形成的线段。

我们对每个障碍物,计算盲区的左右端点(计算射线和直线的交点,具体见代码)并储存。注意盲区的左右端点可能超出边界,所以需要取一下\(\min,\max\)。

我们注意到盲区可能发生重叠,所以需要把重叠部分进行合并,方便计算。

具体方法嘛,就是把盲区按左端点从小到大排序。如果后一个盲区的左端点\(<\)前一个盲区的右端点,就更新前一个盲区的右端点,并且删除后一个盲区。代码使用STL的链表实现。

合并完再遍历一遍,计算最大值即可求出答案。注意遍历前还需要把\((0,0),(len,len)\)分别加到链表的头和尾,因为左右边界也可能存在非盲区。

就酱。

思路并不难,不过代码实现有一些细节需要注意:

  • 盲区左右端点可能超出边界,需要取一下\(\min,\max\)。

  • 合并完盲区,需要额外把\((0,0),(len,len)\)分别加到链表的开头和结尾。

  • 对于所有障碍物,如果其\(y\)坐标\(\ge\)房子的\(y\)坐标,或者\(<\)人行道的\(y\)坐标,就不能参与考虑。注意这一判断与\(x\)坐标无关,就算障碍物完全在人行道外面,也可能在人行道上形成盲区。

  • 遍历链表的过程中,可能会有删除操作。因此我们需要注意一下使用迭代器的方式:

    for(auto it=lis.begin();it!=lis.end();){
    	if(/* 条件 */){
            /* Something ... */
    		it=segs.erase(it);
    	}else{
            /* Something ... */
            it++;
        }
    }
    
  • 保留\(2\)位小数。

Code

POJ的编译器太老了,提交的代码需要经过一番修改。这里就不放修改后的代码了,毕竟重在理解嘛。

点击查看代码
#include<bits/stdc++.h>
#define N 100010
using namespace std;
struct tseg{double x1,x2,y;};
struct point{double x,y;};
tseg house,lin;
int n,siz;
double ans;
void modify(tseg &a){
	a.x1-=lin.x1,a.x2-=lin.x1,a.y-=lin.y;
}//将a的绝对位置转为相对人行道的位置
list<pair<double,double>> segs;
double calc(point a,point b){
	return a.x-a.y*(a.x-b.x)/(a.y-b.y);
}//计算射线AB与x轴的交点
int main(){
	while(cin>>house.x1>>house.x2>>house.y){
		segs.clear(),ans=0;
		if(house.x1==house.x2&&house.x2==house.y&&house.y==0) break;
		cin>>lin.x1>>lin.x2>>lin.y>>n;
		modify(house);
		double len=lin.x2-lin.x1;
		for(int i=1;i<=n;i++){
			tseg ta;
			cin>>ta.x1>>ta.x2>>ta.y;
			modify(ta);
			if(ta.y>=house.y||ta.y<0) continue;
			double x1=calc({house.x1,house.y},{ta.x2,ta.y});
			double x2=calc({house.x2,house.y},{ta.x1,ta.y});
			segs.push_back({min(len,max(0.0,x2)),min(len,max(0.0,x1))});
		}//注意x1,x2需要反序,x2实际是左端点
		segs.sort();
		for(auto it=segs.begin(),lastit=segs.end();it!=segs.end();){
			if(lastit!=segs.end()&&(*lastit).second>=(*it).first){
				(*lastit).second=max((*lastit).second,(*it).second);
				it=segs.erase(it);
			}else lastit=it,it++;
		}//合并盲区,注意特殊的迭代器写法
		segs.push_front({0,0});
		segs.push_back({len,len});
		for(auto it=segs.begin(),lastit=segs.end();it!=segs.end();lastit=it,it++){
			if(it==segs.begin()) continue;
			ans=max(ans,(*it).first-(*lastit).second);
		}
		if(ans==0) cout<<"No View\n";
		else cout<<fixed<<setprecision(2)<<ans<<"\n";
	}
	return 0;
}

标签:题解,lin,POJ2074,segs,盲区,house,x2,Line,x1
From: https://www.cnblogs.com/Sinktank/p/18306217

相关文章

  • CF825F String Compression题解
    思路容易想到是个动态规划。首先设\(f_i\)表示字符串前\(i\)个字符所组成的字符串的答案。状态定义好了,接下来就是考虑如何转移了。因为由\(f_i\)可以得到所有\(f_j\),其中\(i+j\lelen\),转移方程为\(f_i=f_j+x\),其中\(x\)为字符串\(i+1\)至\(j\)的最优压缩。接......
  • [ABC347E] Set Add Query题解
    思路通过读题发现,每个数变化当且仅当这个数在集合内。所以不妨设它被添加进来的时间点为\(L_i\),它被删除的时间点为\(R_i\),所以它被增加的数量就是这段时间内集合数量之和。所以用一个变量\(cnt\)模拟当前集合内有多少个数,前缀和维护即可。具体实现参见代码。代码#include<......
  • CF1898D Absolute Beauty 题解
    思路容易发现,如果\(a_i>b_i\)则将\(a_i\)和\(b_i\)交换。在数轴上标出要交换的四个数的位置若线段\(a_ib_i\)和线段\(a_jb_j\)互不相交,此时交换比两条线段处于其他位置时更优。具体证明这里就不再赘述,其他题解讲的已经很清楚了。所以只需交换最大的\(a_i\)和最小......
  • [ABC338E] Chords 题解
    思路思路还是很显然的,简单总结一下思路:首先,将圆环从点\(1\)到\(2N\)切开,并将其拉直成一条直线。在切开状态下,原来的弦变成了直线上的曲线。我们需要判断这些曲线之间是否存在交点。在切开状态下,曲线之间的交点等价于满足\(A_i<A_j<B_i<B_j\)的不同曲线\(i\)和......
  • [ABC258Ex] Odd Steps 题解
    思路拿到这道题,第一时间肯定想到是\(dp\)题目。朴素DP用\(dp_i\)表示序列和为\(i\)的序列个数。因为原数组由奇数组成,所以\(dp\)只可能由\(dp_{i-1}\),\(dp_{i-3}\)等等转移过来,若\(i\inA\),\(dp_i=0\)。即:\[dp_i=\begin{cases}0&i\inA\\dp_{i-1}+dp_{i-3}+\c......
  • CF1859A题解
    CF1859A题解思路考虑一种极端情况,\(b\)数组内的数全部比\(a\)大,这样也无法整除,所以这就是这道题的突破口。我们让\(b\)数组内的数全部比\(a\)里的大,最方便的实现方法就是把原数组内的最大的数放进\(b\)数组,剩下的放进\(a\)数组。注意特判无解情况:原数组内的数一样,这......
  • [ABC352D]题解
    题意在长为\(n\)的序列\(a\)中找出\(k\)个数,设它们的下表为$p_1\(,\)p_2$到\(p_k\),满足这\(k\)个数从小到大排列过后是一个公差为\(1\)的等差数列。求满足条件的\(k\)个数的最大的\(p\)减去最小的\(p\)最小。输出这个值。思路把数组\(a\)排一遍序,滑动窗......
  • [ABC352E]题解
    思路这里提供一种暴力做法。方法就是当边数到达一个值过后就不加边了。我取的值是\(500000\),实际上可以开大一些,只要\(x\logx\)不超时就行了。代码赛时提交记录#include<bits/stdc++.h>usingnamespacestd;#defineintlonglonginta[200020];intcnt;structrec......
  • P10378 [GESP202403 七级] 交流问题题解
    思路我们把关系想成一张图,每次输入就给两个人连一条边。因为一个人只有两种选择,所以我们在一个联通块内随便找一个点,跑一遍搜索,找出这个联通块内的答案。代码如下。voiddfs(intu,intcolor){cnt2++;//cnt2是这个连通块内的总点数cnt1+=color;//这个是一所学校内......
  • 【题解】金明的预算方案
    原题传送门题目描述金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过\(n\)元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附......