1. 程式人生 > >Noip2016 換教室(數學期望入門)

Noip2016 換教室(數學期望入門)

min 小數 輸出 return 入門題 clas ont 數學期望 std

題意:有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 換教室(數學期望入門)