題目描述
有n個課程安排,其中第i個課程安排是在號教室上課. 每個課程安排都有相應的替代方案,也就是說,我們也可以選擇申請在號教室上同樣的課程. 對於每個課程安排,申請通過的概率爲,而且我們最多可以申請更換個課程的上課地點. 當然,也可以選擇一個都不申請. 與此同時,課間在不同教室之間移動時有一個體力消耗值,現在我們希望求一種申請更換教室的方案,使得體力消耗值的和的期望最小.
題目分析
期望的數學定義:試驗中每次可能結果的概率乘以其結果的總和.
這裏求的期望,等於一種已經確定的選擇方案中,每一步的概率乘以每一步的體力消耗的和.
這是一個以概率加權的平均數形式.
期望dp的基本形式:
80pts:
觀察到測試點中有許多給出的是的形式,把這些拿下就有差不多了.
100pts:
我們不妨考慮動態規劃,設計狀態:表示當前個課程總共選擇了個進行變更時,所得到的期望最小體力消耗.
然而我們發現這樣子不足以完全表示狀態,因爲狀態還與前一個課程是否選擇更換教室有關,如果前一個課程更換了教室,那麼前後兩個課程的體力消耗值會改變. 也就是說,這樣設的狀態產生了後效性. 所幸我們知道,我們可以通過細化狀態轉移方程(即給狀態加維)來消除後效性,那麼我們就增加一維,表示當前這個課程是否更換了教室.
接下來我們研究如何進行狀態轉移.
不妨考慮最簡單的情況,也就是,均不申請更換,那麼期望直接加上兩者之間的體力消耗即可.
然後考慮不申請更換,而申請更換的情況,那麼這樣子就有的可能成功更換,的可能不能成功更換,由於這兩種情況都有可能發生,而期望是所有可能情況的概率乘以體力消耗的和,所以這裏應該把兩者和他們對應的體力消耗的積都加上.
由上面的描述,這種情況的期望是
之後我們需要在以上兩個值中取
同理我們可以得到
這一個狀態轉移方程和上面那個的原理是一樣的,只不過這裏還同時用到了乘法原理(期望+=兩個獨立事件同時發生的概率*對應的體力消耗
)
講完了狀態轉移方程,這道題基本上就結束了. 但是注意初始化所有狀態爲極大值(所有都只能由前一個時間段推知),然後(第一節課更換教室的情況和不更換教室的情況,由於一開始就在教室,所以一開始消耗的體力均爲0),枚舉的當前時間段從2開始循環;在每次循環中,都先要(不提出申請的情況),枚舉的已用申請次數從1開始循環.
程序實現
#include<bits/stdc++.h>
using namespace std;
const int inf=400000;//inf不能開太大也不能開太小
int c[2010],d[2010],n,m,cnt,e;
int g[310][310];
double f[2010][2010][2],p[2010],ans;
int main(){
scanf("%d%d%d%d",&n,&m,&cnt,&e);
for(int i=1;i<=n;i++)scanf("%d",&c[i]);
for(int i=1;i<=n;i++)scanf("%d",&d[i]);
for(int i=1;i<=n;i++)scanf("%lf",&p[i]);
for(int i=1;i<=cnt;i++){
for(int j=1;j<=cnt;j++)g[i][j]=(i==j)?0:inf;
} //初始化鄰接矩陣
for(int i=1,u,v,w;i<=e;i++){
scanf("%d%d%d",&u,&v,&w);
g[u][v]=min(g[u][v],w);
g[v][u]=g[u][v];//注意雙向邊
}
for(int k=1;k<=cnt;k++){
for(int i=1;i<=cnt;i++){
for(int j=1;j<=cnt;j++){
g[i][j]=min(g[i][k]+g[k][j],g[i][j]);
g[j][i]=g[i][j];
}
}
}//Floyd求最短路
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
f[i][j][0]=(double)inf;
f[i][j][1]=(double)inf;
}
}//狀態初始化
f[1][0][0]=0;f[1][1][1]=0;
for(int i=2;i<=n;i++){
f[i][0][0]=f[i-1][0][0]+g[c[i-1]][c[i]];//每一個狀態都只能由前一個推知
for(int j=1;j<=m;j++){//從1開始循環,防止越界
f[i][j][0]=min(f[i-1][j][0]+g[c[i-1]][c[i]],f[i-1][j][1]+p[i-1]*g[d[i-1]][c[i]]+(1-p[i-1])*g[c[i-1]][c[i]]);
//當前課程如果不變
f[i][j][1]=min(f[i-1][j-1][0]+p[i]*g[c[i-1]][d[i]]+(1-p[i])*g[c[i-1]][c[i]],\
f[i-1][j-1][1]+p[i]*p[i-1]*g[d[i-1]][d[i]]+p[i]*(1-p[i-1])*g[c[i-1]][d[i]]+(1-p[i])*p[i-1]*g[d[i-1]][c[i]]+(1-p[i])*(1-p[i-1])*g[c[i-1]][c[i]]);
//當前課程如果更換
}
}
ans=inf;
for(int i=0;i<=m;i++)ans=min(f[n][i][1],min(ans,f[n][i][0]));
//由於沒有要求m個申請全部用完,所以要比較查找
printf("%.2lf\n",ans);
return 0;
}
題後總結
概率/期望dp,一定要走出一個誤區:不要總是想着算出一種方案的所有情況的概率再乘權值. 要用動態規劃的思維去做這些題.
比如說:已知一種方案爲改變第1節課上課地點和第2節課上課地點,那我們不應該先算出對應的四種情況的概率(1成功2成功,1成功2失敗,1失敗2成功,1、2均失敗)再去求期望,最後再比較,而是應該在狀態轉移中表示所有的狀態.