Codeforces 277D (170D) Google Code Jam

問題

t分間にわたってgoogle code jam形式のコンテストを行う。


n問の問題があり、それぞれsmall largeにわかれている。
smallを解くのにかかる時間、largeを解くのにかかる時間が問題ごとに与えられる。
smallを解くと得られる得点、largeを解くと得られる得点も問題ごとに与えられる。


smallは100%の確率で通すことができ、largeは、問題ごとに異なる失敗する確率がある。
失敗はコンテスト終了後に判明する。


得られる得点の期待値を最大にしたい。
得点の期待値が同じ中でも、ペナルティの期待値を最小にしたい。
ペナルティは、最後に正解した問題の時間である。


得点の期待値とペナルティの期待値を求めよ。

制約条件

n≦1000
t≦1600

方針

得点の期待値を最大にするには、
dp[時間]のテーブルを更新するDPをすればよい。


その中で、ペナルティの期待値を最小にすることを考える。
SとLのうち、解くものを決めた時、どういう順序で解くのが最適か。
まず、SSSSSLLLLLのように、Lが最後に並ぶのが明らかに最適であることはわかる。
その中で、Lの順序を決めたい。

二つのL、L1, L2があって、それぞれ解く時間をt1, t2、正解率をp1, p2とすると、
入れ替えた場合に損するのは、
(t1 + t2) * p2 + (1 - p2) * p1 * t1 <
(t1 + t2) * p1 + (1 - p1) * p2 * t2
のとき。
なぜなら、L1, L2と並んでいるときこの二つ以外のLの順序は同一なので無視してよくて、
t1 + t2のペナルティが入るのがp2を正解したとき
t1のペナルティが入るのがp2を不正解でp1を正解したときだから。


したがってこれが成り立つようにソートしてやればよいことがわかる。
使う順序がわかったら、この順序でDPするだけになる。


本番は誤差で落ちた。long doubleにしたら通った。

ソースコード

typdef long double ld;
const ld EPS=1e-12, INF=1e12;

struct P{
	int ss, sl, ts, tl;
	ld p;
	P(int a, int b, int c, int d, ld e):ss(a), sl(b), ts(c), tl(d), p(e){}
	bool operator<(const P &o)const{
		return (tl + o.tl) * o.p + tl * (1 - o.p) * p
		< (tl + o.tl) * p + o.tl * (1 - p) * o.p;
	}
};
int n, t;
pair<ld, ld> dp[1600];

void update(pair<ld, ld> &a, ld x, ld y){
	if(a.first + EPS < x) a = mp(x, y);
	if(abs(a.first - x) < EPS && y + EPS < a.second) a = mp(x, y);
}

int main(){
	cin >> n >> t;
	vector<P> prob;
	rep(i, n){
		int ss, sl, ts, tl;
		ld p;
		cin >> ss >> sl >> ts >> tl >> p;
		prob.pb(P(ss, sl, ts, tl, 1 - p));
	}
	sort(all(prob));
	
	rep(i, n) for(int j = t; j >= 0; j--){
		int ss = prob[i].ss, sl = prob[i].sl;
		int ts = prob[i].ts, tl = prob[i].tl;
		ld p = prob[i].p;
		
		int nj = j + ts;
		if(nj <= t) update(dp[nj], dp[j].first + ss, dp[j].second + ts);
		
		nj = j + ts + tl;
		ld na = dp[j].first, nb = dp[j].second;
		na += ss + p * sl;
		nb = (ts + nb) * (1 - p) + nj * p;
		
		if(nj <= t) update(dp[nj], na, nb);
	}
	pair<ld, ld> ans = mp(0, 0);
	rep(i, t + 1) update(ans, dp[i].first, dp[i].second);
	cout << fixed << setprecision(20) << ans.first << " " << ans.second << endl;
	
	return 0;
}