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;
}