BZOJ4720(NOIP2016)[换教室]题解--期望DP

【链接】
bzoj4720

【题目大意】
给你n个时间段,让你挑选每个时间段,选择要上的课程,对于每一个时间段,有一个上被安排上课程的教室和另一个同样上这个课程的教室,但需要申请,申请有通过的概率,你共有m次申请机会。再给你e条路径组成的无向图,通过每条都会耗费一定体力值。如果当前所处的教室到下一个教室是两个不同的教室,就需要从当前这个教室走到另一个教室并花费一点体力值。求上完所有课程因在教室间移动耗费的体力值的总和的期望值最小。

【解题报告】
此题是一道明显的期望DP。
定义:

f[i][j][0]表示当前在i时刻,用了j次机会,此次没有用申请机会的耗费的体力值的总和的最小期望值。
f[i][j][1]表示当前在i时刻,用了j次机会,此次用一次申请机会的耗费的体力值的总和的最小期望值。

推倒:

f[i][j][0]=min(f[i-1][j][0]+cst[a[i-1]][a[i]],f[i-1][j][1]+c[i-1]*cst[b[i-1]][a[i]]+(1-c[i-1])*cst[a[i-1]][a[i]];
if (j) f[i][j][1]=min(f[i-1][j-1][0]+c[i]*cst[a[i-1]][b[i]]+(1-c[i])*cst[a[i-1]][a[i]],f[i-1][j-1][1]+c[i]*(c[i-1]*cst[b[i-1]][b[i]]+(1-c[i-1])*cst[a[i-1]][b[i]])+(1-c[i])*(c[i-1]*cst[b[i-1]][a[i]]+(1-c[i-1])*cst[a[i-1]][a[i]]));
//c[i]是此次申请的成功概率,对于此次申请有c[i]的概率成功和1-c[i]的概率失败。a[i]数组表示被安排上课程的教室,b[i]表示另一个同样上这个课程的教室。

结果:

ans=min(ans,min(f[n][0~m][0],f[n][0~m][1]));
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=2005,maxe=305;
int n,m,v,e,a[maxn],b[maxn],cst[maxe][maxe];
double c[maxn],ans,f[maxn][maxn][2];
inline int Read()
{
    int res=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') res=res*10+ch-48,ch=getchar();
    return res;
}
int main()
{
    freopen("4720.in","r",stdin);
    freopen("4720.out","w",stdout);
    n=Read(); m=Read(); v=Read(); e=Read();
    for (int i=1; i<=n; i++) a[i]=Read();
    for (int i=1; i<=n; i++) b[i]=Read();
    for (int i=1; i<=n; i++) scanf("%lf",&c[i]);
    memset(cst,63,sizeof(cst));
    for (int i=1; i<=v; i++) cst[i][i]=0;
    for (int i=1; i<=e; i++)
    {
        int x=Read(),y=Read(),z=Read();
        if (z<cst[x][y]) cst[x][y]=cst[y][x]=z;
    }
    for (int k=1; k<=v; k++)
     for (int i=1; i<=v; i++)
      for (int j=1; j<=v; j++)
       if (cst[i][k]+cst[k][j]<cst[i][j]) cst[i][j]=cst[i][k]+cst[k][j];
    memset(f,127,sizeof(f));
    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]+cst[a[i-1]][a[i]],f[i-1][j][1]+c[i-1]*cst[b[i-1]][a[i]]+(1-c[i-1])*cst[a[i-1]][a[i]]);
        if (j) f[i][j][1]=min(f[i-1][j-1][0]+c[i]*cst[a[i-1]][b[i]]+(1-c[i])*cst[a[i-1]][a[i]],f[i-1][j-1][1]+c[i]*(c[i-1]*cst[b[i-1]][b[i]]+(1-c[i-1])*cst[a[i-1]][b[i]])+(1-c[i])*(c[i-1]*cst[b[i-1]][a[i]]+(1-c[i-1])*cst[a[i-1]][a[i]]));
      }
    ans=1e100;
    for (int j=0; j<=m; j++) ans=min(ans,min(f[n][j][0],f[n][j][1]));
    printf("%.2lf",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值