poj1039

题目大意

在一段弯弯曲曲的管道内射一束光,光不会反射,求最远能射到哪里(求横坐标)

解题思路

A掉第一道较为复杂的计算几何题,爽。。

  • 首先

    最优的光线一定是经过上下各一个端点的

    不然的话,假如我们把这束光进行平移,肯定能射得更远
    这样就可以 O ( N 2 ) O(N^2) O(N2)来枚举光线了

  • 接下来考虑判断光能射多远

    • 这束光是“真实的光线”,即在之前不会被阻拦

    • 这束光射出后,在哪里被阻拦
      1. 与某条管道壁“规范”相交,即不相交于端点
      2. 相交于端点,则有可能被阻拦,有可能穿过:
      666

      这只需要判断一下两端点相对与光线的位置就好了,具体见代码

#pragma GCC optimize(2)
#include<cstdio>
#include<algorithm>
#define gt() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
#define Out() (fwrite(St,1,Top,stdout))
#define pt(ch) (Top<1000000?St[Top++]=ch:(Out(),St[(Top=0)++]=ch))
#define Tp double
#define db double
#define Vc Vector
using namespace std;
int Top;static char St[1000000],buf[1000000],*p1=buf,*p2=buf;
const int maxn=25;const db eps=1e-6,INF=-(1e100);
int n;db Ans;
struct Vc{
	Tp x,y;
	Vc operator -(const Vc B){return (Vc){x-B.x,y-B.y};}
	Vc operator +(const Vc B){return (Vc){x+B.x,y+B.y};}
	Vc operator *(const Tp p){return (Vc){x*p,y*p};}
}a[maxn],b[maxn],C,D;
Tp Cross(Vc A,Vc B){return A.x*B.y-A.y*B.x;}
Vc getpos(Vc P,Vc v,Vc Q,Vc w){//两直线交点 
	Vc u=P-Q;
	db t=Cross(w,u)/Cross(v,w);
	return (P+(v*t));
}
bool sameside(Vc A,Vc B){return (Cross(D-C,A-C)*Cross(D-C,B-C)>eps);}//叉积判断在直线同侧 
Tp read(){
	int ret=0;bool f=0;char ch=gt();
	while(ch<'0'||ch>'9') f|=(ch=='-'),ch=gt();
	while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=gt();
	db res=ret;
	if(ch=='.'){db d=1;ch=gt();while(ch>='0'&&ch<='9') res+=(ch-'0')/(d*=10),ch=gt();}
	return f?-res:res;
}
void write(int x){if(x>9) write(x/10);pt(x%10+'0');}
bool check(Vc A,Vc B){return (Cross(C-A,D-A)*Cross(C-B,D-B)<-eps);}//规范相交 
bool Across(Vc A,Vc B){return (Cross(C-A,D-A)*Cross(C-B,D-B)<eps);}//相交 
int main(){
	#ifdef hhh
	freopen("data.in","r",stdin);
	freopen("data.out","w",stdout);
	#endif
	for(n=read();n;n=read()){
		for(int i=1;i<=n;i++) b[i]=a[i]=(Vc){read(),read()},b[i].y-=1.0;
		bool flg=1;Ans=INF;
		for(int i=1;i<=n&&flg;i++)
		  for(int j=1,k;j<=n;j++)if(i!=j){
		  	C=a[i],D=b[j];
		  	for(k=1;k<n;k++) 
			  if(check(a[k],a[k+1])||check(b[k],b[k+1])||sameside(a[k+1],b[k+1])||sameside(a[k],b[k])) break;
		  	if(k==n){flg=0;break;}
		  	if(k>=i&&k>=j){//真实直线 
		  	  db tep_a=INF,tep_b=INF;
		  	  if(Across(a[k],a[k+1])) tep_a=getpos(a[k],a[k+1]-a[k],C,D-C).x;
			  if(Across(b[k],b[k+1])) tep_b=getpos(b[k],b[k+1]-b[k],C,D-C).x;
		  	  if(tep_a>Ans) Ans=tep_a;if(tep_b>Ans) Ans=tep_b;
		  	}
		  }
		if(!flg) puts("Through all the pipe.");
		  else printf("%.2f\n",Ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值