zoukankan      html  css  js  c++  java
  • 【bzoj4720】【noip2016】【换座位】期望dp+Floyd

    这里写图片描述
    [pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=62370736
    wa。。。已经快一年了,重新来做这道题。想当年sort都不会写哩。。

    对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程。在可以选择的课程中,有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)节课结束后,牛牛就会从这节课的教室出发,选择一
    条耗费体力最少的路径前往下一节课的教室。现在牛牛想知道,申请哪几门课程可以使他因在教室间移动耗费的体
    力值的总和的期望值最小,请你帮他求出这个最小值。
    Input
    第一行四个整数n,m,v,e。n表示这个学期内的时间段的数量;m表示牛牛最多可以申请更换多少节课程的教室;
    v表示牛牛学校里教室的数量;e表示牛牛的学校里道路的数量。
    第二行n个正整数,第i(1≤i≤n)个正整数表示c,,即第i个时间段牛牛被安排上课的教室;保证1≤ci≤v。
    第三行n个正整数,第i(1≤i≤n)个正整数表示di,即第i个时间段另一间上同样课程的教室;保证1≤di≤v。
    第四行n个实数,第i(1≤i≤n)个实数表示ki,即牛牛申请在第i个时间段更换教室获得通过的概率。保证0≤ki≤1。
    接下来e行,每行三个正整数aj,bj,wj,表示有一条双向道路连接教室aj,bj,通过这条道路需要耗费的体力值是Wj;
    保证1≤aj,bj≤v,1≤wj≤100。
    保证1≤n≤2000,0≤m≤2000,1≤v≤300,0≤e≤90000。
    保证通过学校里的道路,从任何一间教室出发,都能到达其他所有的教室。
    保证输入的实数最多包含3位小数。
    Output
    输出一行,包含一个实数,四舎五入精确到小数点后恰好2位,表示答案。你的
    输出必须和标准输出完全一样才算正确。
    测试数据保证四舎五入后的答案和准确答案的差的绝对值不大于4*10^-3。(如果你不知道什么是浮点误差,这段话
    可以理解为:对于大多数的算法,你可以正常地使用浮点数类型而不用对它进行特殊的处理)
    Sample Input
    3 2 3 3
    2 1 2
    1 2 1
    0.8 0.2 0.5
    1 2 5
    1 3 3
    2 3 1
    Sample Output
    2.80

    学了期望之后来看这道题,倒还算是有思路了。

    首先,如果不换教室,就是最短路的问题。考虑到要查询多个两点最短距离和数据范围,用Floyd是最好的。

    那么如果申请换教室,就有k的几率换成新路(距离可能会变长也可能变短),也有(1-k)的几率换不成教室。由期望的线性性就可以求出换教室的期望距离。

    剩下的就是dp了。我们设dp[i][j][0/1]表示前i个时间段申请j次,第i个时间段是否申请换教室。转移方程:
    dp[i][j][0]=min(dp[i-1][j][1]+ee(i,0,i-1,1),dp[i-1][j][0]+map[c[i]][c[i-1]]);
    dp[i][j][1]=min(dp[i-1][j-1][1]+ee(i,1,i-1,1),dp[i-1][j-1][0]+ee(i,1,i-1,0));
    其中ee的函数是指期望路程:

    double ee(int a,int sa,int b,int sb){
        if(sa==sb&&sa==0)
            return map[c[a]][c[b]];
        if(sa==1&&sb==0)
            return k[a]*map[d[a]][c[b]]+(1-k[a])*map[c[a]][c[b]];
        if(sa==0&&sb==1)
            return k[b]*map[c[a]][d[b]]+(1-k[b])*map[c[a]][c[b]];
        return k[b]*k[a]*map[d[a]][d[b]]+(1-k[a])*k[b]*map[c[a]][d[b]]+(1-k[b])*k[a]*map[d[a]][c[b]]+(1-k[a])*(1-k[b])*map[c[a]][c[b]];
    }

    然后这道题有些基础的细节忘完了,导致各种地方wa。下面罗列一下:
    1、都申请的情况计算漏了一部分;
    2、Floyd忘了给数组赋初值无穷大;
    3、dp的初值没有考虑清楚
    4、double型数组不能用memset
    wawawawawa。。。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    const int N=2000+5;
    const int V=300+5;
    const int E=90000+5;
    int n,m,v,e,c[N],d[N];
    double map[V][V];
    double dp[N][N][2],k[N];
    
    double ee(int a,int sa,int b,int sb){
        if(sa==sb&&sa==0)
            return map[c[a]][c[b]];
        if(sa==1&&sb==0)
            return k[a]*map[d[a]][c[b]]+(1-k[a])*map[c[a]][c[b]];
        if(sa==0&&sb==1)
            return k[b]*map[c[a]][d[b]]+(1-k[b])*map[c[a]][c[b]];
        return k[b]*k[a]*map[d[a]][d[b]]+(1-k[a])*k[b]*map[c[a]][d[b]]+(1-k[b])*k[a]*map[d[a]][c[b]]+(1-k[a])*(1-k[b])*map[c[a]][c[b]];
    }
    int main(){
        scanf("%d%d%d%d",&n,&m,&v,&e);
        for(int i=1;i<=n;i++) scanf("%d",&c[i]);
        for(int i=1;i<=n;i++) scanf("%d",&d[i]);
        for(int i=1;i<=n;i++) scanf("%lf",&k[i]);
        for(int i=1;i<=v;i++)
            for(int j=1;j<=v;j++) if(i!=j) map[i][j]=0x7fffffff;
        for(int i=1;i<=e;i++){
            int a,b,w;
            scanf("%d%d%d",&a,&b,&w);
            map[a][b]=map[b][a]=min(map[a][b],(double)w);
        }
        for(int k=1;k<=v;k++)
            for(int i=1;i<=v;i++)
                for(int j=1;j<=v;j++){
                        map[i][j]=min(map[i][j],map[i][k]+map[k][j]);
                }
        for(int i=1;i<=n;i++)
            for(int j=0;j<=m;j++)
                dp[i][j][0]=dp[i][j][1]=0x7fffffff;
        dp[1][0][0]=dp[1][1][1]=0;
        for(int i=2;i<=n;i++){
            for(int j=0;j<=min(i,m);j++){
                dp[i][j][0]=min(dp[i-1][j][1]+ee(i,0,i-1,1),dp[i-1][j][0]+map[c[i]][c[i-1]]);
                if(j!=0) dp[i][j][1]=min(dp[i-1][j-1][1]+ee(i,1,i-1,1),dp[i-1][j-1][0]+ee(i,1,i-1,0))
            }
        }
        double ans=0x7fffffff;
        for(int j=0;j<=m;j++){
            ans=min(ans,min(dp[n][j][1],dp[n][j][0]));
        }
        printf("%.2lf
    ",ans);
        return 0;
    }
  • 相关阅读:
    hdu 5446 Unknown Treasure lucas和CRT
    Hdu 5444 Elven Postman dfs
    hdu 5443 The Water Problem 线段树
    hdu 5442 Favorite Donut 后缀数组
    hdu 5441 Travel 离线带权并查集
    hdu 5438 Ponds 拓扑排序
    hdu 5437 Alisha’s Party 优先队列
    HDU 5433 Xiao Ming climbing dp
    hdu 5432 Pyramid Split 二分
    Codeforces Round #319 (Div. 1) B. Invariance of Tree 构造
  • 原文地址:https://www.cnblogs.com/LinnBlanc/p/7763131.html
Copyright © 2011-2022 走看看