csp-s模擬測試101的T3代碼+注釋


因為題目過於大神所以單獨拿出來說。而且既然下發std了頹代碼貌似也不算可恥233

很難講啊,所以還是寫在代碼注釋里面吧

因為比較認真的寫了不少注釋,所以建議縮放到80%觀看,或者拿到gedit上

 1 //不要在意我寫的是“氣”,輸入法找不到那個字。。。  2 #include<cstdio>  3 #include<algorithm>  4 long double C[205][205];int n,g,t,a[205],N;  5 struct Ex{//用於存儲期望值  6 long double cnt,tot;//cnt表示到達這個狀態的總方案數,tot表示這些方案所獲得的神的總和  7 void add(long double e,long double c){tot+=e*c;cnt+=c;}//這里是正推的,所以概率加和的結果不為1,所以期望不能直接相加  8 //期望其實類似於加權平均數。現在你有cnt個方案,和為tot。要加入c個方案,這些方案的平均數是e,所以這c個方案的和為e*c  9 //那么總的方案數就是cnt+c。總的和就是tot+e*c 10 long double E(){return cnt?tot/cnt:0;}//期望值就是總量/方案數啊,可以重載成+=,但是因為參數有2個還得寫pair什么的所以就沒有重載 11 }w[205][205],f[205][205];//全局變量初值為0,所以剛開始的方案數和期望值都是0 12 long double cal(int l,int r,int ord){return ord>n?0:(l+r+1)/2.0;}//計算平均數,表示用了一份l的氣所得到的神的期望。 13 //如果用的是原來不存在的氣,就是你加的那些編號為[n+1,n+t]的氣,這些氣是不存在的,所以其實你放棄了這一份精,所以並不會得到神,返回0 14 int main(){ 15 freopen("surmount.in","r",stdin);freopen("surmount.out","w",stdout); 16 scanf("%d%d%d",&n,&g,&t);//方便查閱:n是氣的份數,g是單次最大的精量,t是輪數 17 for(int i=1;i<=n;++i)scanf("%d",&a[i]); 18 for(int i=1;i<=n;++i)if(a[i]>g)a[i]=g;//大於g的氣是沒有用的,直接重置為g 19 std::sort(a+1,a+1+n);N=n+t;//排序方便處理連續的區間 20 for(int i=n+1;i<=N;++i)a[i]=g;//添加一些足夠大的氣,這樣在下面處理的時候就能保證每一份精都能被壓制 21 for(int i=0;i<=t;++i)C[i][0]=1; 22 for(int i=1;i<=t;++i)for(int j=1;j<=i;++j)C[i][j]=C[i-1][j-1]+C[i-1][j];//處理組合數 23 //w[i][j]表示對於[i,j)這段區間的所有氣,如果使用了它們所獲得的神的期望值 24 for(int i=0;i<=N;++i)w[i][i].add(0,1);//賦初值:空區間的方案數是1,對神的貢獻為0 25 for(int i=N;~i;--i)for(int j=i+1;j<=N&&j-i<=t;++j)for(int k=i;k<j;++k) 26 w[i][j].add(w[i][k].E()+w[k+1][j].E()+cal(a[k+1],a[i],k+1),w[i][k].cnt*w[k+1][j].cnt*(a[k+1]-a[i])*C[j-i-1][k-i]); 27 //現在的操作是要把[i,k)與[k+1,j)合並成[i,j)。假設最后一個使用的精是k。注意區間開閉。 28 //add的兩個參數:期望值就是[i,k)和[k+1,j)這兩段的期望值的和,然而還有使用k所得到的神,你使用它是用來壓制[a[k+1],a[i]]的精,得到對應的神,詳見cal函數 29 //總方案數就是兩邊的方案相乘(乘法計數原理)。還要乘上組合數,因為要考慮這些氣的順序可以顛倒。因為左右兩邊內部已經有序了,所以只需要把兩個混合起來。 30 //就是類似於歸並排序兩個內部有序的數組,總方案數就是C[j-i-1][k-i],表示在所有j-i-1個位置里選出左邊所用的k-i個元素的位置,注意第k個被你強制定為最后一個了所以是j-i還要-1 31 for(int i=0;i<=t;++i)f[i][i]=w[0][i]; 32 //初值,表示前i-1份氣沒有用。超過t沒有意義。 33 for(int i=0;i<=N;++i)for(int k=0;k<=t;++k)if(f[i][k].cnt)for(int j=i+1;j<=N&&k+j-i-1<=t;++j) 34 f[j][k+j-i-1].add(f[i][k].E()+w[i+1][j].E(),f[i][k].cnt*w[i+1][j].cnt*C[k+j-i-1][k]); 35 //i表示轉移之前已經用了幾份氣(含),j表示轉移之后用的氣,k表示轉移了幾輪,所以k+j-i-1就是轉移之后已經經過了的輪數 36 //f[a][b]表示用了前a份氣,已經經過了b輪之后得到的神的期望,所以最后答案是f[N][t] 37 //轉移就是,先看期望,就是已經做完的f[i][k]再加上新的一輪你用了[i+1,j)這一段的氣,這樣所獲得的神 38 //總方案數其實也一樣,和w的轉移比較類似,也是排序了兩個內部有序的過程 39 printf("%.10Lf\n",f[N][t].E()); 40 }//By DeepinC究級壓行+垃圾注釋(硬核25行)

累。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM