1. 程式人生 > >bzoj3029 守衛者的挑戰

bzoj3029 守衛者的挑戰

Description

  打開了黑魔法師Vani的大門,隊員們在迷宮般的路上漫無目的地搜尋著關押applepi的監獄的所在地。突然,眼前一道亮光閃過。“我,Nizem,是黑魔法聖殿的守衛者。如果你能通過我的挑戰,那麼你可以帶走黑魔法聖殿的地圖……”瞬間,隊員們被傳送到了一個擂臺上,最初身邊有一個容量為K的包包。
  擂臺賽一共有N項挑戰,各項挑戰依次進行。第i項挑戰有一個屬性ai,如果ai>=0,表示這次挑戰成功後可以再獲得一個容量為ai的包包;如果ai=-1,則表示這次挑戰成功後可以得到一個大小為1
的地圖殘片。地圖殘片必須裝在包包裡才能帶出擂臺,包包沒有必要全部裝滿,但是隊員們必須把
【獲得的所有的】地圖殘片都帶走(沒有得到的不用考慮,只需要完成所有N項挑戰後揹包容量足夠容納地圖殘片即可),才能拼出完整的地圖。並且他們至少要挑戰成功L次才能離開擂臺。
  隊員們一籌莫展之時,善良的守衛者Nizem幫忙預估出了每項挑戰成功的概率,其中第i項挑戰成功的概率為pi%。現在,請你幫忙預測一下,隊員們能夠帶上他們獲得的地圖殘片離開擂臺的概率。
Input

  第一行三個整數N,L,K。   第二行N個實數,第i個實數pi表示第i項挑戰成功的百分比。揹包容量還剩
  第三行N個整數,第i個整數ai表示第i項挑戰的屬性值. Output

  一個整數,表示所求概率,四捨五入保留6 位小數。

數學期望dp。
dp[i][j][x]表示前i個挑戰,贏下其中j個,揹包容量還剩x的概率。
用刷表法,分成功和失敗兩種情況轉移。
dp[i+1][j+1][tn(x+a[i+1])]+=dp[i][j][tn(x)]*p[i+1];
dp[i+1][j][tn(x)]+=dp[i][j][tn(x)]*(1-p[i+1]);
最後的答案就是把所有j>=l,x>=0的dp累加。
注意因為n比較小,所以容量大了沒有用。
計算時只需要考慮n以內,超過n的當成n算即可。
注意可以出現中間放不下但是最後放下的情況,所以下標x可以為負。
平移解決即可。

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
double dp[202][202][403],p[202];
int a[202],n;
int tn(int x)
{
    if (x>n) x=n;
    return x+201;
}
int main()
{
    int i,j,k,l,m,q,x,y,z;
    double ans=0;
    scanf("%d%d%d",&n,&l,&k);
    for (i=1
;i<=n;i++) { scanf("%d",&x); p[i]=x/100.0; } for (i=1;i<=n;i++) scanf("%d",&a[i]); if (k>n) k=n; dp[0][0][tn(k)]=1; for (i=0;i<n;i++) for (j=0;j<=i;j++) for (x=-i;x<=n;x++) { dp[i+1][j+1][tn(x+a[i+1])]+=dp[i][j][tn(x)]*p[i+1]; dp[i+1][j][tn(x)]+=dp[i][j][tn(x)]*(1-p[i+1]); } for (i=l;i<=n;i++) for (j=0;j<=n;j++) ans+=dp[n][i][tn(j)]; printf("%lf",ans); }