CF_contest385D Bear and Floodlight
http://codeforces.com/contest/385/problem/D
题意:
在一个二维平面内,初始有一只熊想要在晚上从(L, 0)走到(R,0)。你要沿着长度为(R - L)的线段行走。熊Y只在有光的地方行走。
有n个路灯,分布在平面上,照射方向可以任意旋转,照射距离无穷。
现在给出L,R和路灯坐标(Xi, Yi),以及路灯能够照射的角度Ai。路灯摆放好之后就不能再转动,问这只熊最远能够走多少距离。
数据:
保证不会有相同的灯在同一坐标。
(1 <= n <= 20, -1e5 <= L <= R <= 1e5).
(-1000 <= xi <= 1000, 1 <= yi <= 1000, 1 <= ai <= 90).
思路:
1. 状压dp,记dp[S]为使用了集合S内的灯之后向右能走到的最大x坐标。
2. 转移枚举不在集合内的一盏灯i,将其旋转到打在x轴上的线段左端点恰好为dp[S]。
3. 通过解三角形来计算增加的横坐标, 更新dp[S∪{i}].(可能为无穷远)
4. 注意:atan(double) 与 atan2 (double ,double) 之间的精度问题。
【atan2 返回的是方位角,即与 x 轴的夹角,取值范围为(-Pi,Pi] .】
atan2 比 atan 稳定。
如:atan(y/x),当 y 远远大于 x 时,计算结果是不稳定的。
atan2(y,x)的做法:当 x 的绝对值比 y 的绝对值大时使用 atan(y/x);反之使用 atan(x/y)。这样就保证了数值稳定性。
代码:
#include <iostream>
#include <string.h>
#include <cstdio>
#include <algorithm>
#include <math.h>
#include <vector>
#include <queue>
#include <map>
using namespace std;
typedef long long LL;
typedef pair<int,int> P;
const int N = 21;
const int M = 100000;
const double INF = 1e9;
const double Pi = acos(-1);
const double eps = 1e-9;
int n,m,a,b,c;
double l,r;
double dp[1<<N];
struct pt
{
double x,y;
double a;
}s[N];
double ct(double x,double y,double a,double pos)
{
a=a*Pi/180;
double c=atan2(-y,-(x-pos));
if (a+c>-eps)
return INF;
return tan(a+Pi/2+c)*y+x;
}
int main()
{
int cc,cas=1;
while (~scanf("%d%lf%lf",&n,&l,&r))
{
memset(dp,0,sizeof(dp));
dp[0]=l;
for (int i=0;i<n;i++)
scanf("%lf%lf%lf",&s[i].x,&s[i].y,&s[i].a);
for (int i=0;i<(1<<n);i++)
dp[i]=l;
for (int i=0;i<(1<<n);i++)
{
for (int j=0;j<n;j++)
{
if ((i&(1<<j))==0)
dp[i|(1<<j)]=max(dp[i|(1<<j)],ct(s[j].x,s[j].y,s[j].a,dp[i]));
}
}
printf("%.9lf\n",min(dp[(1<<n)-1],r)-l);
}
return 0;
}
/*
5 -10000 2621
906 402 3
334 64 7
-161 565 4
946 548 27
122 466 28
5 -10000 100000
993 94 2
-503 76 2
986 4 2
-312 21 1
338 6 2
2 3 5
3 1 45
5 1 45
1 0 1
1 1 30
1 0 1
1 1 45
1 0 2
0 2 90
*/