Codeforces 385(#226 Div2 only) D. Bear and Floodlight

問題

数直線上y = 0の線を、x = lからx = rまで移動したい。
n個のライトがあって、それぞれの座標は(x[i], y[i])で、
a[i]度の角度の範囲だけを照らすことができる。


移動できる部分は、線分に一つ以上のライトがかかっているところだけ。
最大でどれだけ移動できるか求めよ。

制約条件

n≦20
y[i]は1以上

方針

ビットDP.
dp[bit]をbitのライトを使ったときにいける最も右の位置とする。


位置と次に試すライトを決めたら、その位置がギリギリ入るようにライトを向ければいいので、次の右端がわかる。
これで更新をしていけばばいい。


ライトの範囲がrを超えてしまう場合があるのでrまでにする。

ソースコード

typedef complex<double> P;
int n, l, r;
P p[20];
double a[20];
double dp[1 << 20];

int main(){
	
	cin >> n >> l >> r;
	rep(i, n){
		cin >> p[i].real() >> p[i].imag() >> a[i];
		a[i] *= PI / 180;
	}
	
	rep(i, 1 << n) dp[i] = -INF;
	dp[0] = l;
	
	rep(i, 1 << n) if(dp[i] > -INF) rep(j, n) if(!(i & 1 << j)){
		
		P d = P(dp[i], 0) - p[j];
		
		double ang = arg((P(r, 0) - p[j]) / d);
		ang = min(ang, a[j]);
		
		d *= P(cos(ang), sin(ang));
		d *= -p[j].imag() / d.imag();
		
		dp[i ^ 1 << j] = max(dp[i ^ 1 << j], p[j].real() + d.real());
	}
	cout << setprecision(20) << fixed << 
	dp[(1 << n) - 1] - l << endl;
	
	return 0;
}