https://www.luogu.org/problem/show?pid=1850
题面很长,实质很水的一道期望DP题。题面自带劝退效果。
首先用Floyd算出任意两点的最短路径。然后设f(i,j,0)为前i节课申请更换j节,且不申请第i节时的最小期望;设f(i,j,1)前i节课申请更换j节,且申请第i节时的最小期望。
可得下面这个超长的状转方程:
f(i,j,0)=min{
f(i-1,j,0) + dist(c[i-1],c[i]), // 不申请第i-1节
f(i-1,j,1) + k[i-1]*dist(d[i-1],c[i]) + (1-k[i-1])*dist(c[i-1],c[i]) // 申请第i-1节
}
f(i,j,1)=min{
f(i-1,j-1,0) + k[i]*dist(c[i-1],d[i]) + (1-k[i])*dist(c[i-1],c[i]), // 不申请第i-1节
f(i-1,j-1,1) + k[i-1]*k[i]*dist(d[i-1],d[i]) + (1-k[i-1])*k[i]*dist(c[i-1],d[i]) + k[i-1]*(1-k[i])*dist(d[i-1],c[i]) + (1-k[i-1])*(1-k[i])*dist(c[i-1],c[i]) // 申请第i-1节
}
注意f(i,0,0)意味着一节课都不申请,需要特判f(i,0,0)=f(i-1,0,0)+dist(c[i-1],c[i])
#include <cstdio> #include <algorithm> #define maxv 310 #define maxn 2010 using namespace std; const int inf = 1000; int v, e; // v表示牛牛学校里教室的数量;e表示牛牛的学校里道路的数量 int dist[maxv][maxv]; void add_edge(int from, int to, int weight) { dist[from][to] = min(dist[from][to], weight); //注意重边和自环 dist[to][from] = dist[from][to]; } void floyd() { // d(k,i,j)=min{d(k-1,i,j), d(k-1,i,k)+d(k-1,k,j)} for (int k = 1; k <= v; k++) for (int i = 1; i <= v; i++) for (int j = 1; j <= v; j++) dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]); } int n, m; // n表示这个学期内的时间段的数量;m表示牛牛最多可以申请更换多少节课程的教室 int c[maxn]; // 第i个时间段牛牛被安排上课的教室 int d[maxn]; // 第i个时间段另一间上同样课程的教室 double k[maxn]; // 牛牛申请在第i个时间段更换教室获得通过的概率 void load() { scanf("%d%d%d%d", &n, &m, &v, &e); for (int i = 1; i <= n; i++) scanf("%d", &c[i]); for (int i = 1; i <= n; i++) scanf("%d", &d[i]); 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++) dist[i][j] = inf; dist[i][i] = 0; } int tmp1, tmp2, tmp3; for (int i = 1; i <= e; i++) { scanf("%d%d%d", &tmp1, &tmp2, &tmp3); add_edge(tmp1, tmp2, tmp3); } } double dp[maxn][maxn][2]; int main() { load(); floyd(); for (int i = 1; i <= n; i++) { dp[i][0][0] = dp[i - 1][0][0] + dist[c[i - 1]][c[i]]; // f(i,0,0)意味着一节课都不申请,需要特判 dp[i][0][1] = 2000000; for (int j = 1; j <= m; j++) { // 第1~i节课,申请更换j节,且不申请第i节时的最小期望 dp[i][j][0] = min( dp[i - 1][j][0] + dist[c[i - 1]][c[i]], // 不申请第i-1节 dp[i - 1][j][1] + k[i - 1] * dist[d[i - 1]][c[i]] + (1 - k[i - 1]) * dist[c[i - 1]][c[i]] // 申请第i-1节 ); // 第1~i节课,申请更换j节,且申请第i节时的最小期望 dp[i][j][1] = min( dp[i - 1][j - 1][0] + k[i] * dist[c[i - 1]][d[i]] + (1 - k[i]) * dist[c[i - 1]][c[i]], // 不申请第i-1节 dp[i - 1][j - 1][1] + k[i - 1] * k[i] * dist[d[i - 1]][d[i]] + (1 - k[i - 1]) * k[i] * dist[c[i - 1]][d[i]] + k[i - 1] * (1 - k[i]) * dist[d[i - 1]][c[i]] + (1 - k[i - 1]) * (1 - k[i]) * dist[c[i - 1]][c[i]] // 申请第i-1节 ); } } printf("%.2f", min(dp[n][m][0], dp[n][m][1])); return 0; }