CF-contest385D

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
*/

东方

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值