HNOI2015亚瑟王(期望dp)

题目链接

题目大意

太长不想写qwq

题解

首先会发现,如果我们按照每一轮往后dp是行不通的,因为这样需要表示当前所有牌的选择状态。
于是我们令 f [ i ] [ j ] f[i][j] 表示前 i i 张牌中, j j 张牌被选了的期望伤害是多少。然后我yy了一个递推式,调了一会儿发现这样行不通……因为第 i i 张牌的选择状态依赖于前面某些牌的选择状态。
既然后面依赖前面,那我们就反过来dp呗……
f [ i ] [ j ] f[i][j] 表示 i i n n 中,有 j j 轮被占用的期望伤害是多少。注意这里 j j 不是多少牌被选择,因为可能有一轮什么牌都不选。
于是递推式就比较好办了,我们枚举当前的牌选或不选,如果选,在第几个被选。由于 i i 并不会影响 i i 之前的牌的选择,我们就可以大胆dp了。
f [ i ] [ j ] = ( 1 p [ i ] ) j f [ i + 1 ] [ j ] + k = 1 j p [ i ] ( 1 p [ i ] ) k 1 ( f [ i + 1 ] [ j 1 ] + d [ i ] ) = ( 1 p [ i ] ) j f [ i + 1 ] [ j ] + ( 1 ( 1 p [ i ] ) j ) ( f [ i + 1 ] [ j 1 ] + 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 ( n r ) 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;
}

猜你喜欢

转载自blog.csdn.net/WAautomaton/article/details/87901897