Description
二维平面上有n个探照灯,第i个灯在(xi,yi)出,且可以照亮任意a[i]度角的范围,问这n个灯最多可照亮x轴上区间[l,r]区域从l开始被照亮的连续的最长长度
Input
第一行三个整数n,l,r表示灯的数量和要照亮的范围,之后n行每行三个整数xi,yi,ai分别表示第i个灯的二维坐标和照亮的范围(1<=n<=20,-1e5<=l<=r<=1e5,-1000<=xi<=1000,1<=yi<=1000,1<=ai<=90)
Output
输出[l,r]区域从l开始被照亮的连续的最长长度
Sample Input
2 3 5
3 1 45
5 1 45
Sample Output
2.000000000
Solution
状压DP,一个状态i表示亮灯的集合,i的二进制表示中第j位是1表示第j个灯亮,否则不亮,dp[i]表示i状态最多可以照亮的连续区域长度,转移方程为dp[i&(1 << j)]=max(dp[i&(1 << j)],dp[i]+deal(dp[i],j)),其中i&(1 << j)=0表示第j个灯不在i状态中然后把第j个灯的照射范围放在最右边,deal(dp[i],j)求出第j个灯从dp[i]位置开始照可以照多长的距离,最终dp[(1 << n)-1]即为答案
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define maxn (1<<20)
double l,r,x[20],y[20],a[20],dp[maxn];
int n;
double deal(double x0,int i)
{
double temp=atan((r-x[i])/y[i]);
temp=min(temp,atan((x0-x[i])/y[i])+a[i]);
return x[i]+y[i]*tan(temp);
}
int main()
{
while(~scanf("%d%lf%lf",&n,&l,&r))
{
r-=l;
for(int i=0;i<n;i++)scanf("%lf%lf%lf",&x[i],&y[i],&a[i]),x[i]-=l,a[i]=a[i]/180.*acos(-1.0);
memset(dp,0,sizeof(dp));
int N=1<<n;
for(int i=0;i<N;i++)
for(int j=0;j<n;j++)
if((i&(1<<j))==0)
dp[i^(1<<j)]=max(dp[i^(1<<j)],deal(dp[i],j));
printf("%.10f\n",dp[N-1]);
}
return 0;
}