1. 程式人生 > 其它 >P1850 [NOIP2016 提高組] 換教室 題解

P1850 [NOIP2016 提高組] 換教室 題解

題目大意

\(n\) 個時間段,有 \(2n\) 個課程,在第 \(i\) 個時間段的時候,有兩個教室 \(c_i\)\(d_i\) ,第 \(i\) 個時間段申請換教室通過的概率為 \(k_i\)

可以選擇 \(m\) 個時間段進行換教室,也可以換不到 \(m\) 個教室。

所在大學是一個 \(v\) 個點,\(e\) 條邊的圖,由雙向邊連線,通過每條路的消耗體力不一樣,要求最小化申請後在教室之間移動的體力值總和的期望。

\(\text{Data Range:} 1 \leq m \leq n \leq 2000,v \leq 300\)


首先要求一個任意兩點之間最短路,這個可以直接 \(v^3\)

\(\text{Floyd}\) 進行預處理,用 \(dis_{u,v}\) 表示點 \(u\) 和點 \(v\) 之間的最短路。

然後考慮設立 \(dp_{i,j,0/1}\) 表示前 \(i\) 個時間段中,有 \(j\) 個時間段換了教室,我當前換沒換教室的期望消費體力。

轉移,直接轉移:

\[dp_{i,j,0} = \min(dp_{i-1,j,0} + dis_{c_{i-1},c_i}, dp_{i-1,j,1} + k_{i-1}\times dis_{d_{i-1},c_i} + (1 - k_{i - 1}) \times dis_{c_{i-1}, c_i}) \]

\(dp_{i,j,1}\)

的轉移更著上面的方式也能寫出來吧,但是寫出來太長了,就沒打公式了,可以直接程式碼,反正就四種情況討論一下,然後直接轉移就好了。


// 德麗莎你好可愛德麗莎你好可愛德麗莎你好可愛德麗莎你好可愛德麗莎你好可愛
// 德麗莎的可愛在於德麗莎很可愛,德麗莎為什麼很可愛呢,這是因為德麗莎很可愛!
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
  int x = 0, f = 1;  char ch = getchar();
  while( !isdigit(ch) ) { if(ch == '-') f = -1;  ch = getchar();  }
  while( isdigit(ch) ) {  x = (x << 1) + (x << 3) + (ch ^ 48);  ch = getchar();  }
  return x * f;
}
const int N = 2e5, inf = 1e17;
int n, m, v, e, c[N], d[N];
double k[N], dp[3005][3005][2], ans = inf, dis[305][305];
void chkmin(double &x,double y) { if (y < x) x = y; }
signed main () {
  n = read(), m = read(), v = read(), e = read();
  for (int i = 1; i <= n; i++) c[i] = read(); 
  for (int i = 1; i <= n; i++) d[i] = read();
  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;
  for (int i = 1; i <= e; i++) {
    int a = read(), b = read(), c = read();
    dis[a][b] = dis[b][a] = min(dis[a][b], (double)c);
  }
  for (int k = 1; k <= v; k++)
    for (int i = 1; i <= v; i++)
      for (int j = 1; j <= v; j++)
        dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
  for (int i = 1; i <= v; i++) dis[i][0] = dis[0][i] = dis[i][i] = 0;
  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][1][1] = dp[1][0][0] = 0;
  for (int i = 2; i <= n; i++) {
    dp[i][0][0] = dp[i - 1][0][0] + dis[c[i - 1]][c[i]];
    for (int j = 1; j <= min(i, m); j++) {
      chkmin(dp[i][j][0], dp[i - 1][j][0] + dis[c[i - 1]][c[i]]);
      chkmin(dp[i][j][0], dp[i - 1][j][1] + (double) (1.0 - k[i - 1]) * dis[c[i - 1]][c[i]] + (double) k[i - 1] * dis[d[i - 1]][c[i]]);
      chkmin(dp[i][j][1], dp[i - 1][j - 1][0] + (double) (1.0 - k[i]) * dis[c[i - 1]][c[i]] + (double) k[i] * dis[c[i - 1]][d[i]] );
      chkmin(dp[i][j][1], dp[i - 1][j - 1][1] + (double) k[i - 1] * k[i] * dis[d[i - 1]][d[i]] + (double) k[i - 1] * (1.0 - k[i]) * dis[d[i - 1]][c[i]] + (double) (1.0 - k[i - 1]) * (1.0 - k[i]) * dis[c[i - 1]][c[i]] + (double) (1.0 - k[i - 1]) * k[i] * dis[c[i - 1]][d[i]] );
    }
  }
  for (int i = 0; i <= m; i++) {
    ans = min(ans, dp[n][i][0]); ans = min(ans, dp[n][i][1]);
  }
  printf("%.2lf\n", ans);
  return 0;
}