BZOJ 3029 守護者的挑戰
打開了黑魔法師Vani的大門,隊員們在迷宮般的路上漫無目的地搜尋著關押applepi的監獄的所在地。突然,眼前一道亮光閃過。“我,Nizem,是黑魔法聖殿的守衛者。如果你能通過我的挑戰,那麽你可以帶走黑魔法聖殿的地圖……”瞬間,隊員們被傳送到了一個擂臺上,最初身邊有一個容量為K的包包。
擂臺賽一共有N項挑戰,各項挑戰依次進行.第i項挑戰有一個屬性\(a_i\),如果\(a_i>=0\),表示這次挑戰成功後可以再獲得一個容量為\(a_i\)的包包;如果\(a_i=-1\),則表示這次挑戰成功後可以得到一個大小為1 的地圖殘片。地圖殘片必須裝在包包裏才能帶出擂臺,包包沒有必要全部裝滿,但是隊員們必須把 獲得的所有的地圖殘片都帶走(沒有得到的不用考慮,只需要完成所有N項挑戰後背包容量足夠容納地圖殘片即可)
隊員們一籌莫展之時,善良的守衛者Nizem幫忙預估出了每項挑戰成功的概率,其中第i項挑戰成功的概率為\(p_i\)%。現在,請你幫忙預測一下,隊員們能夠帶上他們獲得的地圖殘片離開擂臺的概率。
\(input\)
第一行三個整數N,L,K.
第二行N個實數,第i個實數\(p_i\)表示第i項挑戰成功的百分比.
第三行N個整數,第i個整數\(a_i\)表示第i項挑戰的屬性值.
\(output\)
一個整數,表示所求概率,四舍五入保留6位小數.
\(0<=N<=200,0<=k<=2000,0<=L<=N\)
\(-1<=a_i<=1000,0<=p_i<=100\)
設\(f[i][j][k]\)表示經過前i項挑戰,當前容量為j,有k項挑戰獲得了勝利的概率.
因為最多只有200場挑戰,每次挑戰最多得到一個大小為1的地圖殘片,所以最多消耗200的背包容量,即j的最小值為-200,我們最多也只需要200的背包容量,即j的最大值為200,因為數組對負下標的操作不方便,所以不妨把\([-200,200]\)平移到\([0,400]\)來,相當於把200看作0,\(j>200\)表示背包容量有剩余,\(j<200\)表示還有碎片裝不下.
轉移只有兩種,即這次挑戰成功/失敗.
int N,L,K,a[205]; double ans,win[205],lose[205]; double f[205][405][205]; int main(){ N=read();L=read();K=read(); if(K>200)K=200; //應該不會有人跟我一樣,寫成if(k>400)k=400吧 //我們只是把[-200,200](看作)移到[0,400] //k本身的意義不變,背包容量最大為200就足夠了 f[1][K+200][0]=1;//初始化 for(int i=1;i<=N;i++){ double ch;cin>>ch; win[i]=(double)ch/100.0; lose[i]=(double)1.0-win[i]; } //把每一次挑戰的勝率和敗率分別用兩個數組存下 for(int i=1;i<=N;i++){ a[i]=read(); } for(int i=1;i<=N;i++) for(int j=0;j<=400;j++) for(int k=0;k<N;k++){ if(f[i][j][k]==0)continue; f[i+1][j][k]+=f[i][j][k]*lose[i]; //這是第i次(也就是前i+1次)挑戰失敗的狀態轉移 if(a[i]>=0){//表示這次勝利且獲得背包容量 int cnt=f[i][j][k]*win[i];//偷懶,後面多次用到 if(j+a[i]>400)//超過400移回來 f[i+1][400][k+1]+=cnt; else f[i+1][j+a[i]][k+1]+=cnt } else f[i+1][j-1][k+1]+=cnt;//表示這次勝利且獲得碎片 } N++; //我們要知道第n次的結果,也就是要知道前n+1次的結果 for(int j=200;j<=400;j++) for(int k=L;k<N;k++) ans+=f[N][j][k]; //j在[200,400]範圍內才表示背包容量有剩余 //因為要至少取得L次勝利,最多獲得 當前的N - 1 次勝利 printf("%.6lf\n",ans); return 0; }
BZOJ 3029 守護者的挑戰