[洛谷P1850]换教室 概率与期望

本文详细解析了一个关于教室更换申请的动态规划问题,重点介绍了如何通过不同时间段的状态转移方程来计算期望行走距离的最小值。文章给出了具体的实现代码,并讨论了在考虑概率的情况下如何进行决策。

题目←
要分清哪些状态是独立的,哪些状态对期望有影响
一开始傻傻的在通过和没通过之间取min……
事实上,在求期望的前提下,真正影响的决策是是否申请
以及万万没想到Floyd打次了
map[i][i] = 0才对


发现当前时间段的状态仅仅可以由上一时间段的状态转移来
上一时间段的情况可能有以下几种

1、申请了换教室,过
2、申请了换教室,没过
3、没申请换教室

如果没有概率且我们要求的只是最大/最小值,就在换、未换两种情况里取min
但发现我们每个时间段需要做的决策不是换/不换
而是申请/不申请
(1、2两种状态不是我们能够决策的,他们出现的概率已经确定)
所以每个时间点就申请/不申请划分状态
dp[i][j][0/1]表示i时间段,总共申请了j段,而第i段申请/未申请

来梳理状态间的关系:
Bi 为原教室, Ci 为更换后的教室
求i - 1 ~ i距离的时候,可能出现的状况有:
1、第i - 1时间段在 Bi1 ,第i时间段在 Bi
2、第i - 1时间段在 Ci1 ,第i时间段在 Bi
3、第i - 1时间段在 Bi1 ,第i时间段在 Ci
4、第i - 1时间段在 Ci1 ,第i时间段在 Ci

当阶段i我们选择不申请时:
第i时间段在 Bi 的概率:1
第i时间段在 Ci 的概率:0

若从第i - 1阶段未申请的状态转移过来
第i - 1时间段在 Bi1 的概率:1
第i - 1时间段在 Ci1 的概率:0
综上,这个转移为
dp[i][j][0] = dp[i - 1][j][0]
+ map[ Bi1 ][ Bi ] * 1 * 1
+ map[ Ci1 ][ Bi ] * 0 * 1
+ map[ Bi1 ][ Ci ] * 1 * 0
+ map[ Ci1 ][ Ci ] * 0 * 0;

类比一下,若从i - 1申请了的状态转移过来
第i时间段在 Bi 的概率:1
第i时间段在 Ci 的概率:0
第i - 1时间段在 Bi1 的概率:P[i - 1]
第i - 1时间段在 Ci1 的概率:1 - P[i - 1]

这个转移为
dp[i][j][0] = dp[i - 1][j][1]
+ map[ Bi1 ][ Bi ] * (1 - P[i]) * 1//i - 1申请失败
+ map[ Ci1 ][ Bi ] * P[i] * 1//i - 1申请成功
+ map[ Bi1 ][ Ci ] * 1 * 0//失败
+ map[ Ci1 ][ Ci ] * 0 * 0;//成功

继续类比
当阶段i我们选择申请时:
转移同样有两种可能,i - 1申请/未申请
i - 1未申请时:
第i时间段在 Bi 的概率:P[i]
第i时间段在 Ci 的概率:(1 - P[i])
第i - 1时间段在 Bi1 的概率:1
第i - 1时间段在 Ci1 的概率:0
这个转移为:
dp[i][j][0] = dp[i - 1][j][0]
+ map[ Bi1 ][ Bi ] * 1 * P[i]
+ map[ Ci1 ][ Bi ] * 0 * P[i]
+ map[ Bi1 ][ Ci ] * 1 * (1 - P[i])
+ map[ Ci1 ][ Ci ] * 0 * (1 - P[i]);

若从i - 1申请转来
第i时间段在 Bi 的概率:P[i]
第i时间段在 Ci 的概率:(1 - P[i])
第i - 1时间段在 Bi1 的概率:P[i - 1]
第i - 1时间段在 Ci1 的概率:1 - P[i - 1]
这个转移为:
dp[i][j][0] = dp[i - 1][j][0]
+ map[ Bi1 ][ Bi ] * P[i - 1] * P[i]
+ map[ Ci1 ][ Bi ] * (1 - P[i - 1]) * P[i]
+ map[ Bi1 ][ Ci ] * P[i - 1] * (1 - P[i])
+ map[ Ci1 ][ Ci ] * (1 - P[i - 1]) * (1 - P[i]);

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 1061109567
#define LL long long
using namespace std;
const int M = 2000 + 50;
LL map[M][M],c;
double dp[M][M][2],P[M];
int n,m,v,e;
int a,b,B[M],C[M];
int main(){
    scanf("%d%d%d%d",&n,&m,&v,&e);
    for(int i = 1;i <= n;i ++){
        scanf("%d",&B[i]);
    }
    for(int i = 1;i <= n;i ++){
        scanf("%d",&C[i]);
    }
    for(int i = 1;i <= n;i ++){
        scanf("%lf",&P[i]);
    }
    memset(map,0x3f,sizeof(map));
    for(int i = 1;i <= v;i ++){
        map[i][i] = 0;
    }
    for(int i = 1;i <= e;i ++){
        scanf("%d%d%lld",&a,&b,&c);
        if(map[a][b] > c)map[a][b] = c,map[b][a] = c;
    }
    for(int k = 1;k <= v;k ++){
        for(int i = 1;i <= v;i ++){
            for(int j = 1;j <= v;j ++){
                if(map[i][j] > map[i][k] + map[k][j] && map[i][j] != INF && map[k][j] != INF)
                    map[i][j] = map[i][k] + map[k][j];
            }
        }
    }
    for(int i = 0;i <= n;i ++){
        for(int j = 0;j <= n;j ++)
        dp[i][j][0] = dp[i][j][1] = INF;
    }
    dp[1][0][0] = dp[1][1][1] = 0;
    for(int i = 2;i <= n;i ++){
        for(int k = min(i,m);k >= 0;k --){
            dp[i][k][0] = min(dp[i][k][0],dp[i - 1][k][0] + map[B[i - 1]][B[i]]);
            dp[i][k][0] = min(dp[i][k][0],dp[i - 1][k][1] + map[B[i - 1]][B[i]]*(1 - P[i - 1])
                + map[C[i - 1]][B[i]]*P[i - 1]);
            //if(i >= 3 && !dp[i][k][0])printf("haha%d\n",i);
            if(k >= 1){
                dp[i][k][1] = min(dp[i][k][1],dp[i - 1][k - 1][0]
                                             + map[B[i - 1]][B[i]]*(1 - P[i]) + map[B[i - 1]][C[i]]*P[i]);
                dp[i][k][1] = min(dp[i][k][1],dp[i - 1][k - 1][1] + map[B[i - 1]][B[i]]*(1 - P[i - 1])*(1 - P[i])
                + map[C[i - 1]][B[i]]*P[i - 1]*(1 - P[i]) + map[B[i - 1]][C[i]]*(1 - P[i - 1])*P[i]
                + map[C[i - 1]][C[i]]*P[i - 1]*P[i]);
            }
        }
    }
    double ans = INF;
    for(int i = 0;i <= m;i ++){
        ans = min(ans,min(dp[n][i][0],dp[n][i][1]));
    }
    /*
    ans = 0;
    for(int i = 2;i <= n;i ++){
        ans += map[B[i - 1]][B[i]];
    }*/
    printf("%.2lf",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值