HDU4336 Card Collector

原题链接:HDU4336

状态压缩,概率 DP

题目大意:$N$ 个物品,每次得到第 $i$ 个物品的概率为 $g[i]$,而且有可能什么也得不到,问期望多少次能收集到全部 $N$ 个物品。

解法

f[state]表示状态state到达目标状态的期望步数。状态中的1表示该物品已经得到,目标为全1状态。dp(state)(记忆化搜索)含义相同。

状态转移

假设当前状态state中还有k个物品没有得到。

$$ f[state] = \sum_{i=1}^{k}g[i]\times[1+dp(state|1<<i)]+(1-\sum_{i=1}^{k}g[i])(1+f[state]) $$

要注意这里的i应该是物品编号,这里这么写只是为了表述方便。
这个式子第一部分的含义是,f[state]到目标的期望步数,state,得到一个没有之前没有的物品i,到达state | 1 << i状态,再加上新状态到目标的步数。第二部分的含义是,state得到了已经有的物品,那么仍然是state状态,相当于原地走了一步。

对上式进行化简,可得:

$$ f[state] = \frac{\sum_{i=1}^{k}g[i]\times dp(state|1<<i)+1}{\sum_{i=1}^{k}g[i]} $$

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

const int N = 21;

int n;
double f[1 << N];
double g[N];

// 状态state到全1状态的期望步数
double dp(int state) {
	if (f[state] >= 0) return f[state];
	double res = 0, sum = 0;
	for (int i = 0; i < n; i++) {
		if (state >> i & 1) continue;
		res += g[i] * dp(state | 1 << i);
		sum += g[i];
	}
	f[state] = (res + 1) / sum;
	return f[state];
}

int main() {
	while (cin >> n) {
		memset(f, -1, sizeof f);
		f[(1 << n) - 1] = 0;
		for (int i = 0; i < n; i++) cin >> g[i];
		printf("%.5f\n", dp(0));
	}
	return 0;
}
updatedupdated2020-05-162020-05-16
加载评论
点击刷新