Luogu P1850 [NOIP2016 提高组] 换教室 (概率DP)

https://www.luogu.com.cn/problem/P1850

  • 因为是无向边,所以正着倒着做期望都行。
  • 定义上到第i节课,精准交了j次申请,当前交没交申请的最小期望时间 f[i][j][0 / 1]
  • 期望题的dp转移,不同决策间取min,一个决策包含了这个决策下的所有可能

一定要和背包的状态区分开,背包是前i个物品剩余体积为j的值, 这个题不一定是操作越多越好,所以第二维不能定义为“剩余操作”,必须是精准操作,这样根据状态的定义又要严格的初始化边界,转移的时候判断非法状态。

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false) ,cin.tie(0), cout.tie(0);
//#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
//#define int long long
const int N = 2e3 + 5;
const int M = 1e6 + 5;
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1004535809;
const double PI = acos(-1.0);
int g[305][305], c[N], d[N];
double p[N];
double f[N][N][2];
void floyd( int n, int m ) {
   for ( int i = 1; i <= n; ++ i) {
       for ( int j = 1; j <= n; ++ j ) {
           if(i == j) g[i][j] = 0; else g[i][j] = 0x3f3f3f3f;
       }
   }
   for ( int i = 1; i <= m; ++ i ) {
       int a, b, w; cin >> a >> b >> w;
       g[a][b] = min(w, g[a][b]), g[b][a] = min(w, g[b][a]);
   }
   for ( int k = 1; k <= n; ++ k ) {
       for ( int i = 1; i <= n; ++ i ) {
           for ( int j = 1; j <= n; ++ j ) {
               g[i][j] = min( g[i][j], g[i][k] + g[k][j] );
           }
       }
   }
}
int main () {
   //IOS
   int n, m, V, E;
   cin >> n >> m >> V >> E;
   for ( int i = 1; i <= n; ++ i ) cin >> c[i];
   for ( int i = 1; i <= n; ++ i ) cin >> d[i];
   for ( int i = 1; i <= n; ++ i ) cin >> p[i];
   floyd(V, E);
   for (int i = 0; i <= n; ++ i) {
       for ( int j = 0; j <= m; ++ j ) {
           f[i][j][0] = f[i][j][1] = 0x3f3f3f3f;
       }
   }
   f[1][0][0] = f[1][1][1] = 0; // 控制合法状态
   for ( int i = 2; i <= n; ++ i ) {
       f[i][0][0] = f[i - 1][0][0] + g[c[i - 1]][c[i]]; // 记得转移j为0的状态
       for ( int j = 1; j <= min(m, i); ++ j ) {
           
           if(i - 1 >= j) f[i][j][0] = min( f[i - 1][j][0] + g[c[i - 1]][c[i]], f[i - 1][j][1]
           + p[i - 1] * g[d[i - 1]][c[i]] + (1 - p[i - 1]) * g[c[i - 1]][c[i]] );
           
           f[i][j][1] = min( f[i - 1][j - 1][0] + p[i] * g[c[i - 1]][d[i]] + (1 - p[i]) * g[c[i - 1]][c[i]],
           f[i - 1][j - 1][1] + p[i] * p[i - 1] * g[d[i - 1]][d[i]] + (1 - p[i]) * p[i - 1] * g[d[i - 1]][c[i]] +
           p[i] * (1 - p[i - 1]) * g[c[i - 1]][d[i]] + (1 - p[i]) * (1 - p[i - 1]) * g[c[i - 1]][c[i]]);
       }
   }
   double ans = 0x3f3f3f3f;
   //根据状态定义,我们要枚举j
   for ( int i = 0;  i <= m; ++ i ) ans = min ( ans, min( f[n][i][1], f[n][i][0] ));
   printf("%.2lf\n", ans);

   return 0;
}
posted @ 2022-05-06 16:59  qingyanng  阅读(18)  评论(0编辑  收藏  举报