链接
https://www.luogu.org/problem/show?pid=2473
题解
状态有些奇怪,不过也是套路。
f[i][j]
表示的是进行了前i轮,物品集合为j(不包含本轮的)的期望值。这里的j相当于一种预估,是一种前继的记录。
如果直接用f[i][]去转移f[i+1][],会出问题,如果f[i][j]这种情形本来就不可能存在,那么其转移也都是不合法的。所以就倒推,倒推时不合法的依然可能存在,但是方程中取max的时候做了裁决,不合法的状态此时一定会被舍掉。
枚举i、枚举j、枚举这轮选的物品now(虽然是这轮选的,但是根据我们的状态设计,它所对应的那个二进制1会被记到f[i+1][]里)。
如果j中具备了选择now的条件,
f[i][j]+=max{f[i+1][j],f[i+1][j|(1<<now−1)]+w[now]}/N
否则
f[i][j]+=f[i+1][j]/N
为了减小精度误差, ÷N 可以放到最后。
代码
//期望dp、状压dp
#include <cstdio>
#include <algorithm>
#define maxn 15
#define maxk 110
using namespace std;
int K, N, pre[maxn+5];
double f[maxk][1<<maxn], w[maxn+5];
void init()
{
int i, x;
scanf("%d%d",&K,&N);
for(i=1;i<=N;i++)
{
scanf("%lf",w+i);
for(scanf("%d",&x);x;scanf("%d",&x))pre[i]|=1<<x-1;
}
}
void dp()
{
int i, j, now;
double ans=0.0;
for(i=K;i;i--)
{
for(j=0;j<(1<<N);j++)
{
for(now=1;now<=N;now++)
{
if((j|pre[now])==j)
f[i][j]+=max(f[i+1][j],f[i+1][j|(1<<now-1)]+w[now]);
else f[i][j]+=f[i+1][j];
}
f[i][j]/=N;
if(i==1)ans+=f[i][j];
}
}
printf("%.6lf",f[1][0]);
}
int main()
{
init();
dp();
return 0;
}