zz: http://www.strongczq.com/2012/03/srm537-div2-3-princextoastbook.html
題目原文:http://community.topcoder.com/stat?c=problem_statement&pm=11356
題目大意: 有人買了N本面包書,編号為0,1,...,N-1。每本面包書都含有一些知識。吃掉一本面包書有可能可以獲得其中的知識。面包書之間會存在知識依賴關系,用int[] p來表示,如果面包書i依賴于面包樹j,則p[i]=j,如果取值為-1則說明該書不依賴于其他書。如果吃一本面包書時已經掌握了它所依賴的書的知識,那麼就可以獲得該書的知識,否則不可以。現在按照随機順序吃這些書,問最終獲得的幾本書的知識的期望值是多少。
資料規模:N的取值範圍為[2,50]
思路: 根據題目的描述,每一本書其實都存在一條知識依賴路徑,例如假設對于書i,存在以下路徑: i1 -> i2 -> i3 -> i 那麼i依賴于i3, i3依賴于i2, i2依賴于i1,i1不依賴于其他書。擷取i的唯一條件是吃書的順序必須是按着箭頭的順序吃。并且如果這條路徑存在環形結構,那麼i的知識必然無法獲得。 根據以上分析,我們可以對每一本書計算其知識被擷取的機率。也就是說,對于書i,它的依賴路徑上的所有書(包括自己)在随機排列中正好按照依賴順序排的機率。如果依賴路徑的長度為n,則該機率值為1/n! 。把所有書的機率值相加就是最終期望值。 寫代碼的時候也特别注意閉環依賴的判斷。本人第一遍代碼中,判斷i存在環形依賴的條件是依賴路徑重新回到i。由于環形依賴的閉環口未必是在i上,例如 i3 -> i1 -> i2 -> i3 -> i,i3是閉環口,是以判斷條件不正确。解決辦法是,可以使用一個boolean數組來記錄每一個節點是否已存在于依賴路徑來找閉環。
Java代碼:
public class PrinceXToastbook
{
public double eat(int[] p)
{
double res = 0.0;
for(int i = 0; i < p.length; ++i){
int len = 1;
int pp = p[i];
boolean[] visit = new boolean[p.length];
visit[i] = true;
while(pp != -1 && !visit[pp]){
visit[pp] = true;
pp = p[pp];
len++;
}
if(pp != -1){
continue;
}
double prop = 1.0;
for(int j = 1; j <= len; ++j){
prop /=j;
}
res += prop;
}
return res;
}
}