P2473 [SCOI2008] 奖励关
很经典的一道 状压Dp+期望 。 这篇题解比较基础些,dalao请绕路 。
我们观察数据范围:
用
接下来我们思考转移方程 :
我们在考虑一个物品时有两个状态 :
- 我们没有达到物品的要求 , 我们没有办法在此轮拿它
- 我们达到了要求,我们在
dp [ i ] [ j ] 要去找dp [ i+1 ] [ j ] 和dp [ i + 1 ] [ k * 2 - 1 ]
如果2情况不太会用代码实现的状压小白可以手玩一些数据 。
比如
我们现有状态为 1011001
.............而物品限制为 1001011
然后 & 出来的结果是 1001001
所以这个物品不可选 。
最后附上注释代码 。
#include<bits/stdc++.h>
using namespace std;
const int N=105;
int n,m;
//n个物品m轮
//第i轮状态为j的最大收益
double dp[N][1<<15|1];
int a[N];//每个物品的值
int limit[N];//第i个物品的限制状态
int main()
{
ios::sync_with_stdio(false);
cin>>m>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
int flag;
while(cin>>flag&&flag)
limit[i]|=(1<<flag-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&limit[k])>=limit[k])
{// 达到了要求
dp[i][j]+=max(dp[i+1][j],dp[i+1][j|(1<<k-1)]+a[k]);
}
else//没有达到要求
dp[i][j]+=dp[i+1][j];
}
//由于我们需要求期望,并且有n个物品
dp[i][j]/=n;
}
}
//控一下小数位,
printf("%.6lf",dp[1][0]);
return 0;
}