1. 程式人生 > >【NOIP2016】換教室

【NOIP2016】換教室

layer 浮點數 min 答案 pro () 大於 row tin

Description

對於剛上大學的牛牛來說,他面臨的第一個問題是如何根據實際情況申請合適的課程。
在可以選擇的課程中,有2n節課程安排在n個時間段上。在第i(1<i<n)個時間段上,兩節內容相同的課程同時在不同的地點進行,其中,牛牛預先被安排在教室ci上課,而另一節課程在教室di進行。
在不提交任何申請的情況下,學生們需要按時間段的順序依次完成所有的n節安排好的課程。如果學生想更換第i節課程的教室,則需要提出申請。若申請通過,學生就可以在第i個時間段去教室di上課,否則仍然在教室ci上課。
由於更換教室的需求太多,申請不一定能獲得通過。通過計算,牛牛發現申請更換第i節課程的教室時,申請被通過的概率是一個己知的實數ki,並且對於不同課程的申請,被通過的概率是互相獨立的。
學校規定,所有的申請只能在學期開始前一次性提交,並且每個人只能選擇至多m節課程進行申請。這意味著牛牛必須一次性決定是否申請更換每節課的教室,而不能根據某些課程的申請結果來決定其他課程是否申請;牛牛可以申請自己最希望更換教室的m門課程,也可以不用完這m個申請的機會,甚至可以一門課程都不申請。
因為不同的課程可能會被安排在不同的教室進行,所以牛牛需要利用課間時間從一間教室趕到另一間教室。
牛牛所在的大學有v個教室,有e條道路。每條道路連接兩間教室,並且是可以雙向通行的。由於道路的長度和擁堵程度不同,通過不同的道路耗費的體力可能會有所不同。當第i(1<=i<=n-1)節課結束後,牛牛就會從這節課的教室出發,選擇一條耗費體力最少的路徑前往下一節課的教室。
現在牛牛想知道,申請哪幾門課程可以使他因在教室間移動耗費的體力值的總和的期望值最小,請你幫他求出這個最小值。

Input

第一行四個整數n, m, v, e . n表示這個學期內的時間段的數量;m表示牛牛最多可以申請更換多少節課程的教室;v表示牛牛學校裏教室的數量;e表示牛牛的學校裏道路的數量。
第二行n個正整數,第i(1<=i<=n)個正整數表示ci,即第i個時間段牛牛被安排上課的教室;保證1<=ci<=v.
第三行n個正整數,第i(1<=i<=n)個正整數表示di,即第i個時間段另一間上同樣課程的教室;保證1<=di<=v .
第四行n個實數,第i(1<=i<=n)個實數表示ki,即牛牛申請在第i個時間段更換教室獲得通過的概率。保證0<=ki<=1。
接下來e行,每行三個正整數aj,bj,wj,表示有一條雙向道路連接教室aj,bj,通過這條道路需要耗費的體力值是Wj;保證1<=aj,bj<=v, 1<=wj<=1000
保證1<=n<=2000,0<=m<=2000,1<=v<=300,0<=e<=90000。
保證通過學校裏的道路,從任何一間教室出發,都能到達其他所有的教室。
保證輸入的實數最多包含3位小數。

Output

輸出一行,包含一個實數,四舍五入精確到小數點後恰好2位,表示答案。你的輸出必須和標準輸出完全一樣才算正確。
測試數據保證四舍五入後的答案和準確答案的差的絕對值不大於4*10^-3(如果你不知道什麽是浮點誤差,這段話可以理解為:對於大多數的算法,你可以正常地使用浮點數類型而不用對它進行特殊的處理)

Sample Input

樣例1輸入:
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

Sample Output

樣例1輸出:
2.80

Hint

提示: 所有可行的申請方案和期望收益如下表

技術分享

1.道路中可能會有多條雙向道路連接相同的兩間教室。也有可能有道路兩端連接的是同一間教室。
2.請註意區分n, m, v, e的意義,n不是教室的數量,m不是道路的數量。

Source

NOIP2016,動態規劃, 概率與期望

題解:   水題一道,但是要知道一些,線性期望性質,這裏科普一下:

  1.隨機變量x與常量c之和的數學期望等於x的期望與這個常量c的和E(x+c)=Ex+c。2.常量c與隨機變量x的乘積等於這個常量與此隨機變量的期望的乘積(cx)=c*Ex。3.隨機變量的線性函數的數學期望等於這個隨機變量期望的同一線性函數即E(kx+c)=kEx+c。

  好了回到本題,我們可以利用以上性質得到到這個點的期望Ex等於到上一個點的期望Ex-1加上通過這條邊的期望,即:Ex=E(x-1)+E(x-1到x),這個很好理解,因為期望本身就是一個平均值,那麽到x所平均消耗的體力==到x-1平均消耗的體力+通過這條邊所平均消耗的體力。

  有了這個性質,我們這個題目就變成了,背包水題了,設dp[i][j][0/1]表示決策到i這個點,用了j次機會,第i個決策是否使用了機會的最小期望。轉移就是利用上面的性質,將從x-1到x的所有情況的期望加起來。即:

  1.dp[i][0][0]=min(dp[i][0][0],dp[i-1][0][0]+dis[c[i-1]][c[i]]);

  2.dp[i][j][0]=min(min(dp[i][j][0],dp[i-1][j][0]+dis[c[i-1]][c[i]]),dp[i-1][j][1]+dis[c[i-1]][c[i]]*(1-p[i-1])+dis[d[i-1]][c[i]]*p[i-1]);

  3.dp[i][j][1]=min(min(dp[i][j][1],dp[i-1][j-1][0]+dis[c[i-1]][d[i]]*p[i]+dis[c[i-1]][c[i]]*(1-p[i])),dp[i-1][j-1][1]+dis[c[i-1]][c[i]]*(1-p[i-1])*(1-p[i])+dis[c[i-1]][d[i]]*(1-p[i-1])*p[i]+dis[d[i-1]][c[i]]*p[i-1]*(1-p[i])+dis[d[i-1]][d[i]]*p[i]*p[i-1]);,自己yy一下就可以了。

代碼:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#define MAXN 2010
#define MAXN2 400
using namespace std;
int n,m,v,e;
int c[MAXN],d[MAXN];double p[MAXN];
int dis[MAXN2][MAXN2];double dp[MAXN][MAXN][2];
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]);
    memset(dis,127/3,sizeof(dis));
    for(int i=0;i<=n;i++)
    for(int j=0;j<=m;j++) dp[i][j][0]=dp[i][j][1]=dis[1][1];
    dp[1][0][0]=dp[1][1][1]=0;
    for(int i=1;i<=e;i++){
        int x,y,z;scanf("%d%d%d",&x,&y,&z);
        dis[x][y]=dis[y][x]=min(dis[x][y],z);
    }
    for(int i=1;i<=v;i++) dis[i][i]=0;
    for(int k=1;k<=v;k++)
        for(int i=1;i<=v;i++)
            for(int j=1;j<=v;j++){
                if(i==j||j==k||k==i) continue;
                dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
            }
    for(int i=2;i<=n;i++){
        dp[i][0][0]=min(dp[i][0][0],dp[i-1][0][0]+dis[c[i-1]][c[i]]);
        for(int j=1;j<=m;j++){
            dp[i][j][0]=min(min(dp[i][j][0],dp[i-1][j][0]+dis[c[i-1]][c[i]]),dp[i-1][j][1]+dis[c[i-1]][c[i]]*(1-p[i-1])+dis[d[i-1]][c[i]]*p[i-1]);
            dp[i][j][1]=min(min(dp[i][j][1],dp[i-1][j-1][0]+dis[c[i-1]][d[i]]*p[i]+dis[c[i-1]][c[i]]*(1-p[i])),dp[i-1][j-1][1]+dis[c[i-1]][c[i]]*(1-p[i-1])*(1-p[i])+dis[c[i-1]][d[i]]*(1-p[i-1])*p[i]+dis[d[i-1]][c[i]]*p[i-1]*(1-p[i])+dis[d[i-1]][d[i]]*p[i]*p[i-1]);
        }
    }
    double ans=dp[n][0][0];
    for(int j=1;j<=m;j++) ans=min(ans,min(dp[n][j][0],dp[n][j][1]));
    printf("%0.2f",ans);
    return 0;
}

【NOIP2016】換教室