[bzoj 4720--noip2016]换教室

124 篇文章 2 订阅
2 篇文章 0 订阅

对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程。在可以选择的课程中,有2n节课程安排在n个时间段上。在第i(1≤i≤n)个时间段上,两节内容相同的课程同时在不同的地点进行,其中,牛牛预先被安排在教室ci上课,而另一节课程在教室di进行。在不提交任何申请的情况下,学生们需要按时间段的顺序依次完成所有的n节安排好的课程。如果学生想更换第i节课程的教室,则需要提出申请。若申请通过,学生就可以在第i个时间段去教室di上课,否则仍然在教室ci上课。由于更换教室的需求太多,申请不一定能获得通过。通过计算,牛牛发现申请更换第i节课程的教室时,申请被通过的概率是一个已知的实数ki,并且对于不同课程的申请,被通过的概率是互相独立的。学校规定,所有的申请只能在学期开始前一次性提交,并且每个人只能选择至多m节课程进行申请。
这意味着牛牛必须一次性决定是否申请更换每节课的教室,而不能根据某些课程的申请结果来决定其他课程是否申请;牛牛可以申请自己最希望更换教室的m门课程,也可以不用完这m个申请的机会,甚至可以一门课程都不申请。因为不同的课程可能会被安排在不同的教室进行,所以牛牛需要利用课间时间从一间教室赶到另一间教室。牛牛所在的大学有v个教室,有e条道路。每条道路连接两间教室,并且是可以双向通行的。由于道路的长度和拥堵程度不同,通过不同的道路耗费的体力可能会有所不同。当第i(1≤i≤n-1)节课结束后,牛牛就会从这节课的教室出发,选择一条耗费体力最少的路径前往下一节课的教室。现在牛牛想知道,申请哪几门课程可以使他因在教室间移动耗费的体力值的总和的期望值最小,请你帮他求出这个最小值。

囧,为什么noip的题目题面怎么都这么长啊。不过,这道题是一道好题,用这个复习了一下期望dp,一般来说期望dp要逆推,但由于这题结局不确定,所以用顺推。f数组很容易想嘛,f[i][j][k],i表示第i段时间,j表示用了j次申请,k就表示这轮是否用申请。
但方程很长很恶心,由于有多种情况,具体看代码。当k为0,说明不用申请,就跟概率无关,但当k为1时,用两种情况,一种就为申请成功,另一种就为申请失败,所以递推式很恶心。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define Inf 0x3f3f3f3f
using namespace std;
int s[310][310];
int a[2010],b[2010];
double c[2010],f[2010][2010][3];
int main()
{
    memset(s,Inf,sizeof(s));
    int n,m,v,e;
    scanf("%d%d%d%d",&n,&m,&v,&e);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)scanf("%d",&b[i]);
    for(int i=1;i<=n;i++)scanf("%lf",&c[i]);
    for(int i=1;i<=v;i++)s[i][i]=0;
    for(int i=1;i<=e;i++)
    {
        int x,y,d;
        scanf("%d%d%d",&x,&y,&d);
        s[x][y]=min(s[x][y],d);
        s[y][x]=min(s[y][x],d);
    }       
    for(int k=1;k<=v;k++)
    {
        for(int i=1;i<=v;i++)
        {
            for(int j=1;j<=v;j++)s[i][j]=s[j][i]=min(s[i][j],s[i][k]+s[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.0;
    for(int i=2;i<=n;i++)
    {
        for(int j=0;j<=min(i,m);j++)
        {
            f[i][j][0]=min(f[i][j][0],f[i-1][j][0]+s[a[i-1]][a[i]]);
            if(j>=1)
            {
                f[i][j][0]=min(f[i][j][0],(f[i-1][j][1]+s[a[i-1]][a[i]])*(1-c[i-1])+(f[i-1][j][1]+s[b[i-1]][a[i]])*c[i-1]);
                f[i][j][1]=min(f[i][j][1],(f[i-1][j-1][0]+s[a[i-1]][a[i]])*(1-c[i])+(f[i-1][j-1][0]+s[a[i-1]][b[i]])*c[i]);
            }
            if(j>=2)
            {
                f[i][j][1]=min(f[i][j][1],(f[i-1][j-1][1]+s[a[i-1]][a[i]])*(1-c[i-1])*(1-c[i])+(f[i-1][j-1][1]+s[b[i-1]][a[i]])*c[i-1]*(1-c[i])+
                (f[i-1][j-1][1]+s[a[i-1]][b[i]])*(1-c[i-1])*c[i]+(f[i-1][j-1][1]+s[b[i-1]][b[i]])*c[i-1]*c[i]);
            }
        }
    }
    double ans=Inf;
    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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值