1. 程式人生 > >[NOIp 2016]換教室

[NOIp 2016]換教室

log clu 誤差 border vertical sam borde hint str

Description

對於剛上大學的牛牛來說,他面臨的第一個問題是如何根據實際情況申請合適的課程。

在可以選擇的課程中,有 $2n$ 節課程安排在 $n$ 個時間段上。在第 $i$($1 \leq i \leq n$)個時間段上,兩節內容相同的課程同時在不同的地點進行,其中,牛牛預先被安排在教室 $c_i$ 上課,而另一節課程在教室 $d_i$ 進行。

在不提交任何申請的情況下,學生們需要按時間段的順序依次完成所有的 $n$ 節安排好的課程。如果學生想更換第 $i$ 節課程的教室,則需要提出申請。若申請通過,學生就可以在第 $i$ 個時間段去教室 $d_i$ 上課,否則仍然在教室 $c_i$ 上課。

由於更換教室的需求太多,申請不一定能獲得通過。通過計算,牛牛發現申請更換第 $i$ 節課程的教室時,申請被通過的概率是一個已知的實數 $k_i$,並且對於不同課程的申請,被通過的概率是互相獨立的。

學校規定,所有的申請只能在學期開始前一次性提交,並且每個人只能選擇至多 $m$ 節課程進行申請。這意味著牛牛必須一次性決定是否申請更換每節課的教室,而不能根據某些課程的申請結果來決定其他課程是否申請;牛牛可以申請自己最希望更換教室的 $m$ 門課程,也可以不用完這 $m$ 個申請的機會,甚至可以一門課程都不申請。

因為不同的課程可能會被安排在不同的教室進行,所以牛牛需要利用課間時間從一間教室趕到另一間教室。

牛牛所在的大學有 $v$ 個教室,有 $e$ 條道路。每條道路連接兩間教室,並且是可以雙向通行的。由於道路的長度和擁堵程度不同,通過不同的道路耗費的體力可能會有所不同。 當第 $i$($1 \leq i \leq n-1$)節課結束後,牛牛就會從這節課的教室出發,選擇一條耗費體力最少的路徑前往下一節課的教室。

現在牛牛想知道,申請哪幾門課程可以使他因在教室間移動耗費的體力值的總和的期望值最小,請你幫他求出這個最小值。

Input

從標準輸入讀入數據。

第一行四個整數 $n,m,v,e$。$n$ 表示這個學期內的時間段的數量;$m$ 表示牛牛最多可以申請更換多少節課程的教室;$v$ 表示牛牛學校裏教室的數量;$e$表示牛牛的學校裏道路的數量。

第二行 $n$ 個正整數,第 $i$($1 \leq i \leq n$)個正整數表示 $c_i$,即第 $i$ 個時間段牛牛被安排上課的教室;保證 $1 \le c_i \le v$。

第三行 $n$ 個正整數,第 $i$($1 \leq i \leq n$)個正整數表示 $d_i$,即第 $i$ 個時間段另一間上同樣課程的教室;保證 $1 \le d_i \le v$。

第四行 $n$ 個實數,第 $i$($1 \leq i \leq n$)個實數表示 $k_i$,即牛牛申請在第 $i$ 個時間段更換教室獲得通過的概率。保證 $0 \le k_i \le 1$。

接下來 $e$ 行,每行三個正整數 $a_j, b_j, w_j$,表示有一條雙向道路連接教室 $a_j, b_j$,通過這條道路需要耗費的體力值是 $w_j$;保證 $1 \le a_j, b_j \le v$, $1 \le w_j \le 100$。

保證 $1 \leq n \leq 2000$,$0 \leq m \leq 2000$,$1 \leq v \leq 300$,$0 \leq e \leq 90000$。

保證通過學校裏的道路,從任何一間教室出發,都能到達其他所有的教室。

保證輸入的實數最多包含 $3$ 位小數。

Output

輸出到標準輸出。

輸出一行,包含一個實數,四舍五入精確到小數點後恰好$2$位,表示答案。你的輸出必須和標準輸出完全一樣才算正確。

測試數據保證四舍五入後的答案和準確答案的差的絕對值不大於 $4 \times 10^{-3}$。 (如果你不知道什麽是浮點誤差,這段話可以理解為:對於大多數的算法,你可以正常地使用浮點數類型而不用對它進行特殊的處理)

Sample Input

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

2.80

Sample Explanation

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

申請更換教室的時間段申請通過的時間段出現的概率耗費的體力值耗費的體力值的期望
1.0 8 8.0
1 1 0.8 4 4.8
0.2 8
2 2 0.2 0 6.4
0.8 8
3 3 0.5 4 6.0
0.5 8
1、2 1、2 0.16 4 4.48
1 0.64 4
2 0.04 0
0.16 8
1、3 1、3 0.4 0 2.8
1 0.4 4
3 0.1 4
0.1 8
2、3 2、3 0.1 4 5.2
2 0.1 0
3 0.4 4
0.4 8

HINT

測試點$n$$m$$v$特殊性質1特殊性質2
1 $\leq1$ $\leq1$ $\leq300$ × ×
2 $\leq2$ $\leq0$ $\leq20$
3 $\leq1$ $\leq100$
4 $\leq2$ $\leq300$
5 $\leq3$ $\leq0$ $\leq20$
6 $\leq1$ $\leq100$ ×
7 $\leq2$ $\leq300$ ×
8 $\leq10$ $\leq0$
9 $\leq1$ $\leq20$ ×
10 $\leq2$ $\leq100$ ×
11 $\leq10$ $\leq300$
12 $\leq20$ $\leq0$ $\leq20$ ×
13 $\leq1$ $\leq100$ ×
14 $\leq2$ $\leq300$
15 $\leq20$ ×
16 $\leq300$ $\leq0$ $\leq20$ ×
17 $\leq1$ $\leq100$
18 $\leq2$ $\leq300$
19 $\leq300$ ×
20 $\leq2000$ $\leq0$ $\leq20$ ×
21 $\leq1$
22 $\leq2$ $\leq100$
23 $\leq2000$
24 $\leq300$
25

特殊性質1:圖上任意兩點 $a_i, b_i$ ($a_i \neq b_i$)間,存在一條耗費體力最少的路徑只包含一條道路。

特殊性質2:對於所有的 $1 \leq i \leq n, k_i = 1$。

時間限制:$1\texttt{s}$

空間限制:$512\texttt{MB}$

題解

1、根據期望的線性特點可以知道總路程的期望等於每相鄰兩節課之間路程的期望相加,一條路徑期望只和前後兩次選課有關,所以我們可以$DP$來做;

2、預處理出任兩點間的最短路;

3、設$f[i][j][k]$為前$i$節課用了$j$次換課機會,$k$取值$true$和$false$,代表第$i$節課有沒有申請換課,$f$值是滿足這些限制條件下的最大期望;

4、轉移就直接枚舉下一節課申不申請換課,根據前後這兩節課是否申請,統計出不同的前後上課地點的概率,算出期望。

5、轉移方程:

技術分享

技術分享

 1 //It is made by Awson on 2017.10.22
 2 #include <set>
 3 #include <map>
 4 #include <cmath>
 5 #include <ctime>
 6 #include <stack>
 7 #include <queue>
 8 #include <vector>
 9 #include <string>
10 #include <cstdio>
11 #include <cstdlib>
12 #include <cstring>
13 #include <iostream>
14 #include <algorithm>
15 #define LL long long
16 #define Min(a, b) ((a) < (b) ? (a) : (b))
17 #define Max(a, b) ((a) > (b) ? (a) : (b))
18 #define Abs(x) ((x) < 0 ? (-(x)) : (x))
19 using namespace std;
20 const int N = 2000;
21 const int V = 300;
22 const int INF = ~0u>>1;
23 
24 int n, m, v, e, a, b, w;
25 int c[N+5], d[N+5];
26 int mp[V+5][V+5];
27 double k[N+5];
28 double f[N+5][N+5][2];
29 
30 void floyd() {
31     for (int k = 1; k <= v; k++)
32         for (int i = 1; i <= v; i++) if (k != i)
33             for (int j = 1; j <= v; j++) if (i != j && k != j)
34                 mp[i][j] = Min(mp[i][j], mp[i][k]+mp[k][j]);
35 }
36 void work() {
37     scanf("%d%d%d%d", &n, &m, &v, &e);
38     for (int i = 0; i <= n; i++) for (int j = 0; j <= m; j++) f[i][j][0] = f[i][j][1] = INF;
39     for (int i = 1; i <= n; i++) scanf("%d", &c[i]);
40     for (int i = 1; i <= n; i++) scanf("%d", &d[i]);
41     for (int i = 1; i <= n; i++) scanf("%lf", &k[i]);
42     memset(mp, 127/3, sizeof(mp));
43     for (int i = 1; i <= v; i++) mp[0][i] = mp[i][i] = 0;
44     for (int i = 1; i <= e; i++) {
45         scanf("%d%d%d", &a, &b, &w);
46         mp[a][b] = mp[b][a] = Min(mp[a][b], w);
47     }
48     floyd();
49     f[0][0][0] = 0;
50     for (int i = 1; i <= n; i++)
51         for (int j = 0; j <= Min(m, i-1); j++) {
52             f[i][j][0] = min(f[i-1][j][0]+mp[c[i-1]][c[i]], f[i-1][j][1]+k[i-1]*mp[d[i-1]][c[i]]+(1-k[i-1])*mp[c[i-1]][c[i]]);
53             f[i][j+1][1] = min(f[i-1][j][0]+k[i]*mp[c[i-1]][d[i]]+(1-k[i])*mp[c[i-1]][c[i]], f[i-1][j][1]+k[i]*k[i-1]*mp[d[i-1]][d[i]]+k[i]*(1-k[i-1])*mp[c[i-1]][d[i]]+(1-k[i])*k[i-1]*mp[d[i-1]][c[i]]+(1-k[i-1])*(1-k[i])*mp[c[i-1]][c[i]]);
54         }
55     double ans = INF;
56     for (int i = 0; i <= m; i++) ans = min(ans, min(f[n][i][0], f[n][i][1]));
57     printf("%.2lf\n", ans);
58 }
59 int main() {
60     work();
61     return 0;
62 }

[NOIp 2016]換教室