Noip2016 換教室(數學期望入門)
題意:有2n個課程安排在n個時間段上,在第i個時間被安排在ci教室上課,但牛牛可以申請換教室,到di教室上課,但只有pi的可能成功。學校有v個教室,e條道路,每條路雙向連通,每條路都會消耗一定的體力wi(保證每個教室可以互相到達),問牛牛應該怎麽安排,才能使總花費的體力的期望值最小。
輸入格式:
第一行四個整數n,m,v,e。n表示時間短的數量;m表示最多可以申請更換教室的數量;v表示教室的數量;e表示道路的數量。
第二行n個正整數,第i個表示ci;
第三行n個正整數,第i個表示di;
第四行n個實數,第i個表示pi;
接下來e行,每行三個正整數ai,bi,wi,表示,有一條雙向道路連通ai與bi,消耗的體力為wi;
保證輸入的實數最多包含三位小數。
輸出格式:
輸出一行,包含一個實數,四舍五入到小數點後兩位,表示答案。
輸入樣例:
3 2 3 3
2 1 2
1 2 1
0.8 0.2 0.5
1 2 5
1 3 3
2 3 1
輸出樣例:
2.80
解析:一道數學期望的入門題,dp的方程不難想,dp[i][j][k] 表示前i個教室,換了j次教室,第i個點有沒有換所走的最小期望距離。顯然 dp[i][j][0] = max(dp[i-1][j][0] + 所需的代價, dp[i-1][j][1] + 所需的代價),dp[i][j][1] = max(dp[i-1][j-1][0] + 所需的代價, dp[i-1][j-1][1] + 所需的代價)。本題的關鍵便在“所需的代價”上(狀態轉移特別毒瘤)。轉移如下:
dp[i][j][0] = max(dp[i-1][j][0] + 上一個教室與這一個教室的距離, dp[i-1][j][1] + 上一次換教室與這一次的距離*上次換教室成功的幾率 + 上一次沒換教室與這一次的距離*上一次換教室不成功的幾率);
dp[i][j][1] = max(dp[i-1][j-1][0] + 上一個教室與這個教室換了教室的距離*這個教室換成功的幾率 + 上一個教室與這個教室的距離*這個教室換不成功的幾率, dp[i-1][j-1][1] + 上一個教室換了與這個教室換了的距離*上個教室換成功的幾率*這個教室換成功的幾率 + 上一個教室與這個教室的距離*上個教室換不成功的幾率*這個教室換不成功的幾率 + 上一個教室換了與這個教室的距離*上個教室換成功的幾率*這個教室換不成功的幾率 + 上個教室與這個教室換了的距離*上個教室換不成功的幾率*這個教室換成功的幾率);
註意:換教室成功的幾率為pi, 則不成功的幾率則為(1-pi)。
轉移特別麻煩,而且也特別長。
代碼如下:
#include<cstdio> #include<algorithm> using namespace std; int n,m,v,e,w[301][301],c[2001],d[2001]; double p[2001],dp[2001][2001][2],ans=2e9; void floyd(void) { //floyd預處理每個教室之間的距離 for (int k=1;k<=v;++k) for (int i=1;i<=v;++i) for (int j=1;j<=v;++j) w[i][j]=min(w[i][j],w[i][k]+w[k][j]); } int main() { scanf("%d%d%d%d",&n,&m,&v,&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<=v;++i) for (int j=1;j<i;++j) w[i][j]=w[j][i]=1e9; for (int i=1;i<=e;++i) { int x,y,v; scanf("%d%d%d",&x,&y,&v); w[x][y]=w[y][x]=min(w[x][y],v); //處理重邊 } floyd(); for (int i=1;i<=n;++i) for (int j=0;j<=m;++j) dp[i][j][1]=dp[i][j][0]=1e9; dp[1][0][0]=dp[1][1][1]=0; //dp初始化 for (int i=2;i<=n;++i) for (int j=0;j<=min(i,m);++j) { //惡心的轉移 dp[i][j][0]=min(dp[i][j][0],min(dp[i-1][j][0]+w[c[i-1]][c[i]],dp[i-1][j][1]+w[d[i-1]][c[i]]*p[i-1]+w[c[i-1]][c[i]]*(1-p[i-1]))); if (j>0) dp[i][j][1]=min(dp[i][j][1],min(dp[i-1][j-1][0]+w[c[i-1]][d[i]]*p[i]+w[c[i-1]][c[i]]*(1-p[i]),dp[i-1][j-1][1]+w[d[i-1]][d[i]]*p[i-1]*p[i]+w[c[i-1]][c[i]]*(1-p[i-1])*(1-p[i])+w[c[i-1]][d[i]]*(1-p[i-1])*p[i]+w[d[i-1]][c[i]]*p[i-1]*(1-p[i]))); } for (int i=0;i<=m;++i) ans=min(ans,min(dp[n][i][1],dp[n][i][0])); //不一定要用完所有換教室的機會,所以取min printf("%.2lf",ans); return 0; }
Noip2016 換教室(數學期望入門)