显然要记f[i][s]表示做到第k次,此时已有的物品集合为S的期望得分。下文前者指时间靠前的状态,后者指时间靠后的状态。
如果正向DP,即f[i-1] -> f[i],会出问题。因为这样意味着第i次的决策可以根据第i次出现的不同物品来改变第i-1次的决策。即我们枚举第i次出现的物品,对i-1的不同s取max,就会后者决定前者。
应当反向DP,即f[i+1] -> f[i]。也就是前者根据后者不同情况的不同答案来决定自己的后继,是前者决定后者。答案就是f[1][0]。
#include<cstdio>
#include<algorithm>
#define N 16
#define K 105
using namespace std;
namespace runzhe2000
{
const double INF = 1e9;
int s[N], p[N];
double f[K][1<<N];
void main()
{
int k, n; scanf("%d%d",&k,&n);
for(int i = 1; i <= n; i++)
{
scanf("%d",&p[i]);
while(1)
{
char ch = getchar(); int r = 0;
for(; ch < '0' || ch > '9'; ch = getchar());
while(ch >= '0' && ch <= '9') r = r * 10 + ch - '0', ch = getchar();
if(r) s[i] |= 1<<(r-1);
else break;
}
}
int sta = 1<<n;
for(int i = k; i; i--)
{
for(int j = 0; j < sta; j++)
{
for(int k = 1; k <= n; k++)
{
if((j&s[k]) == s[k])
f[i][j] += (1.0/n) * max( f[i+1][j], f[i+1][ j | (1<<(k-1)) ] + p[k] );
else f[i][j] += (1.0/n) * f[i+1][j];
}
}
}
printf("%.6lf\n",f[1][0]);
}
}
int main()
{
runzhe2000::main();
}