【问题描述】
打开了黑魔法师Vani 的大门,队员们在迷宫般的路上漫无目的地搜寻着关押applepi 的
监狱的所在地。突然,眼前一道亮光闪过。“我,Nizem,是黑魔法圣殿的守卫者。如果你能通过我的挑战,那么你可以带走黑魔法圣殿的地图„„”瞬间,队员们被传送到了一个擂台上,最初身边有一个容量为K 的包包。擂台赛一共有N 项挑战,各项挑战依次进行。第i项挑战有一个属性ai ,如果 ai>=0,表示这次挑战成功后可以再获得一个容量为ai的包包;如果ai=-1,则表示这次挑战成功后可以得到一个大小为1 的地图残片。地图残片必须装在包包里才能带出擂台,包包没有必要全部装满,但是队员们必须把获得的所有的地图残片都带走(没有得到的不用考虑,只需要完成所有N 项挑战后背包容量足够容纳地图残片即可),才能拼出完整的地图。并且他们至少要挑战成功L 次才能离开擂台。队员们一筹莫展之时,善良的守卫者Nizem 帮忙预估出了每项挑战成功的概率,其中第i 项挑战成功的概率为pi。现在,请你帮忙预测一下,队员们能够带上他们获得的地图残片离开擂台的概率。
【问题分析】
动态规划求解 考场上首先想到的就是F[i][j][k][l]表示前i个挑战之中获胜j次,当前地图有k个,背包容量为l的概率。但是根据此题数据范围n<=200,会TLE,所以想了一下,可以将k与l压维,新的状态为F[i][j][k]表示前i个挑战之中获胜j次,背包还可以装k个地图的概率,此时-200<=k<=200,所以就巧妙地合并了k l俩维。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=210;
int n,k,l,zero=210; int a[N];
double p[N],ans; double F[2][N][N*2];
int readin()
{
int x=0,f=1; char ch=getchar();
while(ch>'9'||ch<'0') {if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int mymax(int a,int b)
{
return a>b?a:b;
}
void read()
{
int i;
n=readin(); l=readin(); k=readin();
k=min(k,n);
for (i=1;i<=n;i++)
{
p[i]=readin();
p[i]/=100.00;
}
for (i=1;i<=n;i++)
a[i]=readin();
return;
}
void dp()
{
int i,j,x; int now=1,last=0;
F[1][1][mymax(n,zero+k+a[1])]=p[1];
F[1][0][k+zero]=1-p[1];
for (i=2;i<=n;i++)
{
swap(now,last);
memset(F[now],0,sizeof(double)*N*N*2);
for (x=zero-n;x<=zero+200;x++)//当j==0时可知之前无胜利 特殊的转移
F[now][0][x]+=(1-p[i])*F[last][0][x];
for (j=1;j<=n;j++)
for (x=zero-n;x<=zero+200;x++)
{
F[now][j][x]+=p[i]*F[last][j-1][x-a[i]];
F[now][j][x]+=(1-p[i])*F[last][j][x];
}
}
for (i=zero;i<=zero+200;i++)
for (j=l;j<=n;j++)
ans+=F[now][j][i];
printf("%.6lf\n",ans);
return;
}
int main()
{
freopen("guard.in","r",stdin);
freopen("guard.out","w",stdout);
read();
dp();
return 0;
}