P1850 換教室
題目描述
對於剛上大學的牛牛來說,他面臨的第一個問題是如何根據實際情況申請合適的課程。
在可以選擇的課程中,有 2n2n 節課程安排在 nn 個時間段上。在第 ii(1 \leq i \leq n1≤i≤n)個時間段上,兩節內容相同的課程同時在不同的地點進行,其中,牛牛預先被安排在教室 c_ic?i?? 上課,而另一節課程在教室 d_id?i?? 進行。
在不提交任何申請的情況下,學生們需要按時間段的順序依次完成所有的 nn 節安排好的課程。如果學生想更換第 ii 節課程的教室,則需要提出申請。若申請通過,學生就可以在第 ii 個時間段去教室 d_id?i?? 上課,否則仍然在教室 c_ic?i?? 上課。
由於更換教室的需求太多,申請不一定能獲得通過。通過計算,牛牛發現申請更換第 ii 節課程的教室時,申請被通過的概率是一個已知的實數 k_ik?i??,並且對於不同課程的申請,被通過的概率是互相獨立的。
學校規定,所有的申請只能在學期開始前一次性提交,並且每個人只能選擇至多 mm 節課程進行申請。這意味著牛牛必須一次性決定是否申請更換每節課的教室,而不能根據某些課程的申請結果來決定其他課程是否申請;牛牛可以申請自己最希望更換教室的 mm 門課程,也可以不用完這 mm 個申請的機會,甚至可以一門課程都不申請。
因為不同的課程可能會被安排在不同的教室進行,所以牛牛需要利用課間時間從一間教室趕到另一間教室。
牛牛所在的大學有 vv 個教室,有 ee 條道路。每條道路連接兩間教室,並且是可以雙向通行的。由於道路的長度和擁堵程度不同,通過不同的道路耗費的體力可能會有所不同。 當第 ii(1 \leq i \leq n-11≤i≤n−1)節課結束後,牛牛就會從這節課的教室出發,選擇一條耗費體力最少的路徑前往下一節課的教室。
現在牛牛想知道,申請哪幾門課程可以使他因在教室間移動耗費的體力值的總和的期望值最小,請你幫他求出這個最小值。
輸入輸出格式
輸入格式:
第一行四個整數 n,m,v,en,m,v,e。nn 表示這個學期內的時間段的數量;mm 表示牛牛最多可以申請更換多少節課程的教室;vv 表示牛牛學校裏教室的數量;ee表示牛牛的學校裏道路的數量。
第二行 nn 個正整數,第 ii(1 \leq i \leq n1≤i≤n)個正整數表示 c_ic?i??,即第 ii 個時間段牛牛被安排上課的教室;保證 1 \le c_i \le v1≤c?i??≤v。
第三行 nn 個正整數,第 ii(1 \leq i \leq n1≤i≤n)個正整數表示 d_id?i??,即第 ii 個時間段另一間上同樣課程的教室;保證 1 \le d_i \le v1≤d?i??≤v。
第四行 nn 個實數,第 ii(1 \leq i \leq n1≤i≤n)個實數表示 k_ik?i??,即牛牛申請在第 ii 個時間段更換教室獲得通過的概率。保證 0 \le k_i \le 10≤k?i??≤1。
接下來 ee 行,每行三個正整數 a_j, b_j, w_ja?j??,b?j??,w?j??,表示有一條雙向道路連接教室 a_j, b_ja?j??,b?j??,通過這條道路需要耗費的體力值是 w_jw?j??;保證 1 \le a_j, b_j \le v1≤a?j??,b?j??≤v, 1 \le w_j \le 1001≤w?j??≤100。
保證 1 \leq n \leq 20001≤n≤2000,0 \leq m \leq 20000≤m≤2000,1 \leq v \leq 3001≤v≤300,0 \leq e \leq 900000≤e≤90000。
保證通過學校裏的道路,從任何一間教室出發,都能到達其他所有的教室。
保證輸入的實數最多包含 33 位小數。
輸出格式:
輸出一行,包含一個實數,四舍五入精確到小數點後恰好22位,表示答案。你的輸出必須和標準輸出完全一樣才算正確。
測試數據保證四舍五入後的答案和準確答案的差的絕對值不大於 4 \times 10^{-3}4×10?−3??。 (如果你不知道什麽是浮點誤差,這段話可以理解為:對於大多數的算法,你可以正常地使用浮點數類型而不用對它進行特殊的處理)
輸入輸出樣例
輸入樣例#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輸出樣例#1:
2.80
說明
【樣例1說明】
所有可行的申請方案和期望收益如下表:
【提示】
- 道路中可能會有多條雙向道路連接相同的兩間教室。 也有可能有道路兩端連接
的是同一間教室。
2.請註意區分n,m,v,e的意義, n不是教室的數量, m不是道路的數量。
特殊性質1:圖上任意兩點 ai, bi, ai≠ bi間,存在一條耗費體力最少的路徑只包含一條道路。
特殊性質2:對於所有的1≤ i≤ n, ki= 1 。
483 055 310
Noip歷年以來的第一道概率題
我一開始想到了24分的做法,就是m=0的情況,
還有一種80分的做法,暴力枚舉換不換。
100分的做法是DP
用dp[i][j][0]表示前i個時間段,已經換了j個,第i個不換的情況,dp[i][j][1]表示換的情況,
轉移方程需要考慮:
1.這次換不換
2.上次換沒換
3.對於每一種情況的期望,
然後根據期望具有線性的原理,
累加即可
註意在讀入邊的時候需要特殊判斷一下
#include<cstdio> #include<cstring> #include<queue> #include<algorithm> using namespace std; const int MAXN=2001; const int INF=0x7ffff; inline void read(int &n) { char c=getchar();n=0;bool flag=0; while(c<‘0‘||c>‘9‘) c==‘-‘?flag=1,c=getchar():c=getchar(); while(c>=‘0‘&&c<=‘9‘) n=n*10+c-48,c=getchar();flag==1?n=-n:n=n; } int n,m,v,e; int C[MAXN],D[MAXN]; double K[MAXN];// 概率 double dis[MAXN][MAXN]; double dp[MAXN][MAXN][3]; inline void floyed() { /*for(int k=1;k<=v;k++) for(int i=1;i<=v;i++) for(int j=1;j<=v;j++) if(dis[i][j]>dis[i][k]+dis[k][j]) dis[i][j]=dis[i][k]+dis[k][j];*/ for(int k=1;k<=v;k++) for(int i=1;i<=v;i++) if(k!=i) for(int j=1;j<=v;j++) if(i!=j && j!=k) dis[j][i]=dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); for(int i=0;i<=n;i++) { for(int j=0;j<=m;j++) dp[i][j][0]=dp[i][j][1]=INF; } dp[1][0][0]=0;dp[1][1][1]=0; } inline void DP() { for(int i=2;i<=n;i++)// 每一個時間段 { for(int j=0;j<=m;j++)// 可以提出申請的次數 { if(j==0) dp[i][j][0]=dp[i-1][j][0]+dis[C[i]][C[i-1]]; else { dp[i][j][0]=min( dp[i-1][j][0] + dis[C[i]][C[i-1]] , dp[i-1][j][1] + dis[C[i]][D[i-1]] * K[i-1] + dis[C[i]][C[i-1]] * (1-K[i-1])); // 本次不提出申請 dp[i][j][1]=min( dp[i-1][j-1][0] + dis[D[i]][C[i-1]] * K[i] + dis[C[i]][C[i-1]] * (1-K[i]) , dp[i-1][j-1][1] + dis[D[i]][D[i-1]] * K[i] * K[i-1] + dis[D[i]][C[i-1]] * K[i] * (1-K[i-1]) + dis[C[i]][D[i-1]] * (1-K[i]) * K[i-1] + dis[C[i]][C[i-1]] * (1-K[i]) * (1-K[i-1])); } } } double ans=438438438; for(int j=0;j<=m;j++) ans=min(ans,min(dp[n][j][0],dp[n][j][1])); printf("%.2lf",ans); } int main() { //freopen("classrooma.in","r",stdin); //freopen("classrooma.out","w",stdout); read(n);read(m);read(v);read(e); for(int i=1;i<=n;i++) read(C[i]); for(int i=1;i<=n;i++) read(D[i]); for(int i=1;i<=n;i++) scanf("%lf",&K[i]); for(int i=1;i<=v;i++) { for(int j=1;j<=v;j++) dis[i][j]=INF; dis[i][i]=0; } for(int i=1;i<=e;i++) { int x,y;double z; read(x);read(y);scanf("%lf",&z); dis[y][x]=dis[x][y]=min(dis[x][y],z); } floyed(); DP(); return 0; }
P1850 換教室