这题状态和转移都很好想,但是问题在于转移顺序。
一开始我是自然的考虑正推的……考虑到会出现一些不合法的情况,我使用bool数组来记录当前状态是否可用。但是这样出现的问题在于,因为最终要计算平均答案,很多种选择情况 它的终止集合是不同的,而答案也不是把他们简单的加起来……
于是我们考虑倒推……倒推就不会存在有状态不合法的情况了。用(dp[i][S])表示第1轮到第i−1轮内宝物是否取过的状态为S,第i轮到第k轮的最大期望得分。
这样的话转移方程就是:(dp[i][S] += max(dp[i+1][S],dp[i+1][S | (1<<(q-1))] + v[q]) / n)和(dp[i][S] += dp[i+1][S] / n) 前者是可以转移,后者是不能。
答案显然是(dp[1][0]).
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('
')
#define pr pair<int,int>
#define mp make_pair
#define fi first
#define sc second
using namespace std;
typedef long long ll;
const int M = 100005;
const int N = 10000005;
int read()
{
int ans = 0,op = 1;char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();}
while(ch >='0' && ch <= '9') ans = ans * 10 + ch - '0',ch = getchar();
return ans * op;
}
int k,n,S[20],num,x;
double dp[105][1<<16],p,v[20],ans;
int main()
{
k = read(),n = read(),p = (double)(n);
rep(i,1,n)
{
scanf("%lf",&v[i]);
x = read();
while(x) S[i] |= (1<<(x-1)),x = read();
}
per(i,k,1)
{
rep(j,0,(1<<n)-1)
{
rep(q,1,n)
{
if((S[q] & j) == S[q])
dp[i][j] += max(dp[i+1][j],dp[i+1][j | (1<<(q-1))] + v[q]) / p;
else dp[i][j] += dp[i+1][j] / p;
}
}
}
printf("%.6lf
",dp[1][0]);
return 0;
}