洛谷题单指南-概率与统计-P1850 [NOIP 2016 提高组] 换教室
原题链接:https://www.luogu.com.cn/problem/P1850
题意解读:n个时段,每个时段i有一门课程,同时在ci(默认)、di两个地点上课,地点之间转换要耗费体力,且能选择最多m个时段课程进行地点更换(c换d),每个时段更换成功概率为pi,求上完所有课耗费最小体力值的期望。
解题思路:
1、暴搜
直观上,可以先在n个时段中搜索所有m个时段,再枚举每个时段不换、换成功、换失败三种状态,计算每种状态下,地点之间切换的体力值期望,更新最小值。总体复杂度是O(2^n * 2^m * n)。
2、DP
由于到一个地点的体力值只与上一个地点有关,已经耗费的体力不影响后续的体力耗费,可以通过动态规划来解决。
- 状态表示:
设f[i][j][k]表示前i个时段,申请更换j个地点,第i个课程的申请状态为k所耗费的最小体力值的期望;
特别的,k=0表示第i个课程不换,k=1表示第i个课程换(可能换成功,也可能失败)。
设s[i][j]表示从教室i到教室j的最小体力值,也就是最短路径,可以通过Floyd进行预处理。
- 状态转移:
f[i][j][0] = min(
f[i-1][j][0] + s[c[i-1]][c[i]], //上一个教室没换
f[i-1][j][1] + s[d[i-1]][c[i]] * p[i-1] + s[c[i-1]][c[i]] * (1-p[i-1]) //上一个教室换了,可能换成功、也可能没成功
)
注意,j=0时,下面的式子无意义。
f[i][j][1] = min(
f[i-1][j-1][0] + s[c[i-1]][c[i]] * (1-p[i]) + s[c[i-1]][d[i]] * p[i], //上一个教室没换,当前教室换了,可能成功或不成功
f[i-1][j-1][1] + s[c[i-1]][c[i]] * (1-p[i-1]) * (1-p[i]) + s[c[i-1]][d[i]] * (1-p[i-1]) * p[i] + s[d[i-1]][c[i]] * p[i-1] * (1-p[i]) + s[d[i-1]][d[i]] * p[i-1] * p[i] //上一个教室换了、当前教室换了,有四种组合
)
- 初始化:
需要注意floyd的初始化,数组INF不能太大,避免加法溢出,1e9合适
f的初始化,初始为INF,第一个时段没有消耗体力,f[1][0][0] =f[1][1][1] =0
- 答案:
所有的f[n][0]~f[n][m],取最小值。
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 2005, V = 305, INF = 1e9;
int n, m, v, e;
int s[V][V];
double f[N][N][2];
int c[N], d[N];
double p[N];
void floyd()
{
for(int k = 1; k <= v; k++)
for(int i = 1; i <= v; i++)
for(int j = 1; j <= v; j++)
s[i][j] = min(s[i][j], s[i][k] + s[k][j]);
}
void dp()
{
for(int i = 0; i <= n; i++)
for(int j = 0; j <= m; j++)
f[i][j][0] = f[i][j][1] = LLONG_MAX;
f[1][0][0] = f[1][1][1] = 0;
for(int i = 2; i <= n; i++)
{
for(int j = 0; j <= min(m, i); j++)
{
f[i][j][0] = min(
f[i-1][j][0] + s[c[i-1]][c[i]],
f[i-1][j][1] + s[d[i-1]][c[i]] * p[i-1] + s[c[i-1]][c[i]] * (1 - p[i-1])
);
if(j != 0)
f[i][j][1] = min(
f[i-1][j-1][0] + s[c[i-1]][c[i]] * (1 - p[i]) + s[c[i-1]][d[i]] * p[i],
f[i-1][j-1][1] + s[c[i-1]][c[i]] * (1 - p[i-1]) * (1 - p[i]) + s[c[i-1]][d[i]] * (1 - p[i-1]) * p[i] + s[d[i-1]][c[i]] * p[i-1] * (1 - p[i]) + s[d[i-1]][d[i]] * p[i-1] * p[i]
);
}
}
}
int main()
{
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];
for(int i = 1; i <= v; i++)
{
for(int j = 1; j <= v; j++)
{
if(i == j) s[i][j] = 0;
else s[i][j] = INF;
}
}
for(int i = 1; i <= e; i++)
{
int a, b, w;
cin >> a >> b >> w;
s[a][b] = s[b][a] = min(s[a][b], w);
}
floyd();
dp();
double ans = LLONG_MAX;
for(int i = 0; i <= m; i++) ans = min(ans, min(f[n][i][0], f[n][i][1]));
printf("%.2lf", ans);
}
浙公网安备 33010602011771号