洛谷 P2473 [SCOI2008] 奖励关
Description
Solution
状压\(dp\) \(+\) 概率\(dp\)
看数据范围 \(n \leq 15\),考虑状压。
我们把每一个宝物的前提宝物进行状态压缩。
设计 \(dp\) 状态:
\(f[i][j]\) 表示进行到第 \(i\) 轮,获得宝物状态为 \(j\) 时,能获得的最大收益。
不难发现,
当宝物 \(k\) 的前提宝物全部被选取时: \(f[i][j] += max(f[i - 1][j], f[i - 1][j | (1 << (k - 1))])\)
反之 \(f[i][j] += f[i - 1][j]\)
但是这样写有一点小问题。
可能在第 \(i\) 轮时,无法选取到状态 \(j\)。
所以我们要倒着循环转移。
Code
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 110;
int m, n;
int a[N], sta[N];
double f[N][1 << 15];
int main(){
scanf("%d%d", &m, &n);
for(int i = 1; i <= n; i++){
int x;
scanf("%d", &a[i]);
while(scanf("%d", &x) && x)
sta[i] |= (1 << (x - 1));
}
for(int i = m; i >= 1; i--)
for(int j = 0; j < (1 << n); j++){
for(int k = 1; k <= n; k++){
if((j & sta[k]) == sta[k])
f[i][j] += max(f[i + 1][j], f[i + 1][j | (1 << (k - 1))] + a[k]);
else f[i][j] += f[i + 1][j];
}
f[i][j] /= n;
}
printf("%.6lf\n", f[1][0]);
return 0;
}