HNOI2015亞瑟王(期望dp)

WAautomaton發表於2019-02-24

題目連結

題目大意

太長不想寫qwq

題解

首先會發現,如果我們按照每一輪往後dp是行不通的,因為這樣需要表示當前所有牌的選擇狀態。
於是我們令f[i][j]f[i][j]表示前ii張牌中,jj張牌被選了的期望傷害是多少。然後我yy了一個遞推式,調了一會兒發現這樣行不通……因為第ii張牌的選擇狀態依賴於前面某些牌的選擇狀態。
既然後面依賴前面,那我們就反過來dp唄……
f[i][j]f[i][j]表示iinn中,有jj輪被佔用的期望傷害是多少。注意這裡jj不是多少牌被選擇,因為可能有一輪什麼牌都不選。
於是遞推式就比較好辦了,我們列舉當前的牌選或不選,如果選,在第幾個被選。由於ii並不會影響ii之前的牌的選擇,我們就可以大膽dp了。
f[i][j]=(1p[i])jf[i+1][j]+k=1jp[i](1p[i])k1(f[i+1][j1]+d[i])=(1p[i])jf[i+1][j]+(1(1p[i])j)(f[i+1][j1]+d[i])f[i][j]=(1-p[i])^jf[i+1][j]+\sum_{k=1}^jp[i](1-p[i])^{k-1}(f[i+1][j-1]+d[i])\\ =(1-p[i])^jf[i+1][j]+\left(1-(1-p[i])^j\right)(f[i+1][j-1]+d[i])
於是直接O(nr)O(nr)dp就沒了。目前跑得飛快

#include <bits/stdc++.h>
using namespace std;

int T, n, r, d[225];
double f[225][135], p[225];
int main() {
	for (scanf("%d", &T); T--;) {
		scanf("%d%d", &n, &r);
		for (int i = 1; i <= n; i++)
			scanf("%lf%d", p + i, d + i);
		for (int i = 0; i <= r; i++) f[n + 1][i] = 0;
		for (int i = n; i > 0; i--) {
			double mul = 1, pp = 1 - p[i];
			for (int j = 1; j <= r; j++) {
				mul *= pp;
				f[i][j] = (1 - mul) * (f[i + 1][j - 1] + d[i]) + mul * f[i + 1][j];
			}
		}
		printf("%.10lf\n", f[1][r]);
	}
	return 0;
}

相關文章