题意:有 n节课程,每个课程有两个上课地点,初始都是在第一个上课地点上课,对于每一节课程,都可以有一个申请切换上课地点的要求,申请有一定的概率通过或不通过,但是一共只能有 m次申请的机会,而且对于每一节课程只能申请一次,而人在各个教室之间移动时,都要耗费体力值,求最小的体力耗费期望。
解法:期望+DP;
-
52pts做法(简单期望):题目给了很多 m==0 或 m==1 的情况,直接按照题目模拟即可。我们可以将题目抽象成这样的形式:题目给有一张图,注意到点数只有 300,可以想到先用 Floyd求出每个点之间的最短路(因为要求的是最小的体力耗费期望),然后课程就相当于给了你两条上课安排表,其中每一个课程一一对应,可以申请将 A表中的上课地点换成对应的 B表中的上课地点,然后按照这样的地点顺序从开始走到结尾,因为 m==1/0 ,所以可以很简单地模拟得出;
-
100pts做法:可以看到 m的范围可以达到 2000,那么此时就不能简单的模拟了,易想到 DP方程:f[i][j][0/1],表示截止到第 i节课程,已经申请过了 j次,0/1表示第 i节课是否申请,那么接下来就可以写状态转移方程了:
-
本题的分类讨论主要分成两大类进行考虑。
-
一、当前教室没有申请
-
如果前一教室有申请: f[i][j][0]=min(f[i−1][j][1]f[i][j][0]=min(f[i-1][j][1]f[i][j][0]=min(f[i−1][j][1]
-
(1)成功:+k[i−1]∗dis[d[i−1]][c[i]]+k[i-1]*dis[d[i-1]][c[i]]+k[i−1]∗dis[d[i−1]][c[i]]
-
(2)失败:+(1−k[i−1])∗dis[c[i−1]][c[i]]+(1-k[i-1])*dis[c[i-1]][c[i]]+(1−k[i−1])∗dis[c[i−1]][c[i]]
-
-
如果前一教室没有申请:,f[i−1][j][0],f[i-1][j][0],f[i−1][j][0],一定是前后均失败:+dis[c[i−1]][c[i]])+dis[c[i-1]][c[i]])+dis[c[i−1]][c[i]])
-
-
二、当前教室有申请
-
如果前一教室有申请:
f[i][j][1]=min(f[i−1][j−1][1]f[i][j][1]=min(f[i-1][j-1][1]f[i][j][1]=min(f[i−1][j−1][1]
-
(1)前后均成功:+k[i−1]∗k[i]∗dis[d[i−1]][d[i]]+k[i-1]*k[i]*dis[d[i-1]][d[i]]+k[i−1]∗k[i]∗dis[d[i−1]][d[i]]
-
(2)前成功、后失败:+k[i−1]∗(1−k[i])∗dis[d[i−1]][c[i]]+k[i-1]*(1-k[i])*dis[d[i-1]][c[i]]+k[i−1]∗(1−k[i])∗dis[d[i−1]][c[i]]
-
(3)前失败、后成功:+(1−k[i−1])∗k[i]∗dis[c[i−1]][d[i]]+(1-k[i-1])*k[i]*dis[c[i-1]][d[i]]+(1−k[i−1])∗k[i]∗dis[c[i−1]][d[i]]
-
(4)前后均失败:+(1−k[i−1])∗(1−k[i])∗dis[c[i−1]][c[i]]+(1-k[i-1])*(1-k[i])*dis[c[i-1]][c[i]]+(1−k[i−1])∗(1−k[i])∗dis[c[i−1]][c[i]]
-
-
如果前一教室没有申请:,f[i−1][j−1][0],f[i-1][j-1][0],f[i−1][j−1][0]
-
(1)后成功:+k[i]∗dis[c[i−1]][d[i]]+k[i]*dis[c[i-1]][d[i]]+k[i]∗dis[c[i−1]][d[i]]
-
(2)后失败:+(1−k[i])∗dis[c[i−1]][c[i]])+(1-k[i])*dis[c[i-1]][c[i]])+(1−k[i])∗dis[c[i−1]][c[i]])
-
-
这道题在赋初值时要注意,先全部赋成最大值,然后将 f[1][0][0] 和 f[1][1][1] 赋成0;
注意!!!:f[i][j][0/1] 中的 j为已对当前 i进行了 0/1 的选择后所得到的值,这样对上面的赋初值就不难理解了。
下面附上代码:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N = 2005; const int inf = 233333333; int n,m,E,V; int c[N],b[N],g[N][N]; double p[N],f[N][N][2],ans=inf; 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>>b[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) g[i][j]=inf; for(int i=1;i<=V;++i) g[i][i]=0; for(int x,y,z,i=1;i<=E;++i){ cin>>x>>y>>z; if(x==y) continue; g[x][y]=g[y][x]=min(g[x][y],z); } for(int k=1;k<=V;++k) for(int i=1;i<=V;++i) for(int j=1;j<=V;++j) if(i!=j||i!=k||j!=k) g[i][j]=min(g[i][j],g[i][k]+g[k][j]); for(int i=1;i<=n;++i) for(int j=0;j<=m;++j) f[i][j][0]=f[i][j][1]=inf; f[1][0][0]=f[1][1][1]=0; for(int i=2;i<=n;++i) for(int j=0;j<=min(i,m);++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[b[i-1]][c[i]] + (1-p[i-1])*g[c[i-1]][c[i]]); if(j!=0){ f[i][j][1]=min(f[i-1][j-1][0] + p[i]*g[c[i-1]][b[i]] + (1-p[i])*g[c[i-1]][c[i]], f[i-1][j-1][1] + p[i-1]*p[i]*g[b[i-1]][b[i]] + (1-p[i-1])*(1-p[i])*g[c[i-1]][c[i]] + p[i-1]*(1-p[i])*g[b[i-1]][c[i]] + (1-p[i-1])*p[i]*g[c[i-1]][b[i]]); } } for(int i=0;i<=m;++i) ans=min(ans,min(f[n][i][0],f[n][i][1])); printf("%.2lf\n",ans); return 0; }