noip2016 換教室
題目描述
對於剛上大學的牛牛來說, 他面臨的第一個問題是如何根據實際情況中情合適的課程。
在可以選擇的課程中,有2n節課程安排在n個時間段上。在第 i ( 1≤ i≤n)個時同段上, 兩節內容相同的課程同時在不同的地點進行, 其中, 牛牛預先被安排在教室 ci上課, 而另一節課程在教室 di進行。
在不提交任何申請的情況下,學生們需要按時間段的順序依次完成所有的n節安排好的課程。如果學生想更換第i節課程的教室,則需要提出中情。若申請通過,學生就可以在第 i個時間段去教室 di上課, 否則仍然在教室 ci上課。
由於更換教室的需求太多, 申請不一定能獲得通過。 通過計算, 牛牛發現申請更換第 i節課程的教室時, 中情被通過的概率是一個已知的實數 ki, 並且對於不同課程的申請, 被通過的概率是互相獨立的。
學校規定, 所有的申請只能在學期開始前一次性提交, 並且每個人只能選擇至多m節課程進行申請。 這意味著牛牛必須一次性決定是否申請更換每節課的教室, 而不能根據某些課程的申請結果來決定其他課程是否申請; 牛牛可以申請白己最希望更換教室的 m門課程,也可以不用完這m個中情的機會,甚至可以一門課程都不申請。
因為不同的課程可能會被安排在不同的教室進行, 所以牛牛需要利用課問時間從一間教室趕到另一間教室。
牛牛所在的大學有 v個教室,有 e條道路。每條道路連接兩間教室, 並且是可以雙向通行的。 由於道路的長度和擁;i者程度不同, 通過不同的道路耗費的體力可能會有所不同。當第i ( 1≤i≤n-1 )節課結束後,牛牛就會從這節課的教室出發,選擇一條耗費體力最少的路徑前往下一節課的教室。
現在牛牛想知道,申請哪幾門課程可以使他因在教室問移動耗費的體力值的總和的期望值最小,請你幫他求出這個最小值。
輸入輸出格式
輸入格式:
第一行四個整數 n,m,v,e 。 n表示這個學期內的時間段的數量; m表示牛牛最多可以申請更換多少節課程的教室; v表示牛牛學校裏教室的數量; e表示牛牛的學校裏道路的數量。
第二行n個正整數,第 i ( 1≤ i≤ n)個正整數表示c,,即第 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≤100 。
保證1≤n≤2000, 0≤m≤2000, 1≤v≤300, 0≤ e≤90000。
保證通過學校裏的道路,從任何一間教室出發,都能到達其他所有的教室。
保證輸入的實數最多包含3位小數。
輸出格式:
輸出一行,包含一個實數,四舎五入精確到小數點後恰好2位,表示答案。你的
輸出必須和標準輸出完全一樣才算正確。
測試數據保證四舎五入後的答案和準確答案的差的絕對值不大於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 。
分析:一開始不懂期望的概念,現在大致可以理解為一個事件發生的概率*這個事件的權值,理解了概念,就比較容易做了。一開始可以想到爆搜,即每個時間段到底提不提交申請,這樣的話會TLE.其實可以用dp,設dp[i][j][0]為前i個時間段已經提交了j個申請,第i個時間段不提交申請的最小期望值,dp[i][j][1]為前i個時間段已經提交了j個申請,第i個時間段提交申請的最小期望值。那麽dp[i][j][0] = min(dp[i - 1][j][0] + map[c[i - 1]][c[i]], dp[i - 1][j][1] + map[d[i - 1]][c[i]] * k[i - 1] + map[c[i - 1]][c[i]] * (1.0 - k[i - 1])),我們可以這麽理解:如果第i-1個時間段不提交,直接從原本的i-1個教室走到第i個教室,如果第i-1個時間段提交申請的話,那麽有k[i-1]的概率能夠更換到教室,還有1 - k[i-1]的概率不能換到教室,根據期望值可以相加的性質,就能求出來。然後可以發現dp[i][j][1]的處理是類似的 dp[i][j][1] = min(dp[i][j][1],min(dp[i - 1][j - 1][0] + map[c[i - 1]][d[i]] * k[i] + map[c[i - 1]][c[i]] * (1.0 - k[i]), dp[i - 1][j - 1][1] + map[c[i - 1]][c[i]] * (1.0 - k[i - 1]) * (1.0 - k[i]) + map[c[i - 1]][d[i]] * (1.0 - k[i - 1]) * k[i] + map[d[i - 1]][c[i]] * k[i - 1] * (1 - k[i]) + map[d[i - 1]][d[i]] * k[i - 1] * k[i])).
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <queue> #include <string> using namespace std; int n, m, v, e, c[2010], d[2010], map[310][310]; double dp[2010][2010][2]; double k[2010],ans; 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", &k[i]); for (int i = 1; i <= v; i++) for (int j = 1; j <= v; j++) if (i != j) map[i][j] = 1000000000; for (int i = 1; i <= e; i++) { int a, b, w; scanf("%d%d%d", &a, &b, &w); map[a][b] = min(map[a][b], w); map[b][a] = map[a][b]; } //floyd for (int k = 1; k <= v; k++) for (int i = 1; i <= v; i++) for (int j = 1; j <= v; j++) if (map[i][k] + map[k][j] < map[i][j]) map[i][j] = map[i][k] + map[k][j]; for (int i = 1; i <= n; i++) for (int j = 0; j <= m; j++) dp[i][j][0] = dp[i][j][1] = 1e30; dp[1][0][0] = dp[1][1][1] = 0; for (int i = 2; i <= n; i++) for (int j = 0; j <= m; j++) { dp[i][j][0] = min(dp[i - 1][j][0] + map[c[i - 1]][c[i]], dp[i][j][0]); if (j >= 1) { dp[i][j][0] = min(dp[i - 1][j][0] + map[c[i - 1]][c[i]], dp[i - 1][j][1] + map[d[i - 1]][c[i]] * k[i - 1] + map[c[i - 1]][c[i]] * (1.0 - k[i - 1])); dp[i][j][1] = min(dp[i][j][1],min(dp[i - 1][j - 1][0] + map[c[i - 1]][d[i]] * k[i] + map[c[i - 1]][c[i]] * (1.0 - k[i]), dp[i - 1][j - 1][1] + map[c[i - 1]][c[i]] * (1.0 - k[i - 1]) * (1.0 - k[i]) + map[c[i - 1]][d[i]] * (1.0 - k[i - 1]) * k[i] + map[d[i - 1]][c[i]] * k[i - 1] * (1 - k[i]) + map[d[i - 1]][d[i]] * k[i - 1] * k[i])); } } ans = 1e30; for (int i = 0; i <= m; i++) ans = min(ans, min(dp[n][i][1], dp[n][i][0])); printf("%.2lf", ans); return 0; }
noip2016 換教室