[P1850][NOIP2016] 换教室 (期望+DP+Floyd)

题意:有 n节课程,每个课程有两个上课地点,初始都是在第一个上课地点上课,对于每一节课程,都可以有一个申请切换上课地点的要求,申请有一定的概率通过或不通过,但是一共只能有 m次申请的机会,而且对于每一节课程只能申请一次,而人在各个教室之间移动时,都要耗费体力值,求最小的体力耗费期望。

解法:期望+DP;

  1. 52pts做法(简单期望):题目给了很多 m==0 或 m==1 的情况,直接按照题目模拟即可。我们可以将题目抽象成这样的形式:题目给有一张图,注意到点数只有 300,可以想到先用 Floyd求出每个点之间的最短路(因为要求的是最小的体力耗费期望),然后课程就相当于给了你两条上课安排表,其中每一个课程一一对应,可以申请将 A表中的上课地点换成对应的 B表中的上课地点,然后按照这样的地点顺序从开始走到结尾,因为 m==1/0 ,所以可以很简单地模拟得出;

  2. 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[i1][j][1]

      • (1)成功:+k[i−1]∗dis[d[i−1]][c[i]]+k[i-1]*dis[d[i-1]][c[i]]+k[i1]dis[d[i1]][c[i]]

      • (2)失败:+(1−k[i−1])∗dis[c[i−1]][c[i]]+(1-k[i-1])*dis[c[i-1]][c[i]]+(1k[i1])dis[c[i1]][c[i]]

    • 如果前一教室没有申请:,f[i−1][j][0],f[i-1][j][0],f[i1][j][0],一定是前后均失败:+dis[c[i−1]][c[i]])+dis[c[i-1]][c[i]])+dis[c[i1]][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[i1][j1][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[i1]k[i]dis[d[i1]][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[i1](1k[i])dis[d[i1]][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]]+(1k[i1])k[i]dis[c[i1]][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]]+(1k[i1])(1k[i])dis[c[i1]][c[i]]

    • 如果前一教室没有申请:,f[i−1][j−1][0],f[i-1][j-1][0],f[i1][j1][0]

      • (1)后成功:+k[i]∗dis[c[i−1]][d[i]]+k[i]*dis[c[i-1]][d[i]]+k[i]dis[c[i1]][d[i]]

      • (2)后失败:+(1−k[i])∗dis[c[i−1]][c[i]])+(1-k[i])*dis[c[i-1]][c[i]])+(1k[i])dis[c[i1]][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;
}

猜你喜欢

转载自www.cnblogs.com/nnezgy/p/11732282.html