• 【学术篇】NOIP2016 D1T3 luogu1850换教室


    题目链接:点击这里献出你宝贵的时间(是用来做题不是捐赠Emmmm)..

    Emmmm我太弱了= =

    做完这题我觉得我应该去打星际..这题怎么就有重边了呢..

    这题就是一道期望= =当时考场上好像完全不会期望,dp一直也都不会推式子= =
    然后就用floyd水掉了没有期望的点,分数还是太少了,最后就省二滚粗了..

    现在觉得应该在NOIP2017之前学习一下期望,然后就想起了这道题,lhr大佬之前也做了这道题但好像没做完??然后就和我一起做= =他代码跑飞快我也不知道为啥= =

    好吧言归正传,这题就是floyd+dp,当然了这题当然和期望有关,你要接下去看我就默认你对期望有所了解了哈= =不然去找度娘问问去= = 数学期望

    由于我们要求出多源最短路,所以floyd.. 而且v<=300不是嘛..
    n和v真的挺坑的,刚开始还RE了不少遍啊= =

    Emmmm,我们发现由于只有上一节课的选择会影响到即将加一条边的期望值,所以我们可以dp(其实我不知道为什么嘛~)
    我们设f[i][j][0/1]表示处理第i节课,已经申请了j次,这次选择/不选择申请的期望…
    这样的话状态转移方程就呼之欲出了(不存在的,年轻人,要多想):
    然而要做很复杂的分类讨论:

    *我们先做一下符号的声明:
    *- pre[i]表示第i节本来的安排
    *- now[i]表示第i次申请能换到的教室
    *- p[i]表示第i次申请通过的概率
    *- dis[i][j]表示i,j间的距离

    • 这次不申请,我们更新f[i][j][0]的值,那么新加的边的终点就是pre[i]..
      1.如果上次不申请,那么新加的边的起点就是pre[i-1],不需要考虑期望..
      2.如果上次申请了,那么成功了起点就是now[i-1],不成功起点就是pre[i-1]
      所以:
    f[i][j][0]=min(
        f[i-1][j][0]+dis[pre[i-1]][pre[i]],
        f[i-1][j][1]+dis[now[i-1]][pre[i]]*p[i-1]  //若上一次申请成功
        +dis[pre[i-1]][pre[i]]*(1-p[i-1])    //若上一次申请不成功
        //根据期望的性质易得,申请成功时的期望*成功率+不成功的期望*不成功率
    );
    • 而这次申请的话,我们更新f[i][j][1]的值,
      1.如果上次不申请,起点就是pre[i-1]…终点还是根据期望的性质推:
    f[i-1][j-1][0]+ //这次申请所以之前申请j-1次
        dis[pre[i-1]][now[i]]*p[i]  //这次申请成功
        +dis[pre[i-1]][pre[i]]*(1-p[i]) //这次申请不成功

    2.如果上次申请,起点和终点我们就要根据期望的性质枚举一下(太麻烦了= =)

    f[i-1][j-1][1]+
        dis[now[i-1]][now[i]]*p[i-1]*p[i] //两次申请都成功
        +dis[now[i-1]][pre[i]]*p[i-1]*(1-p[i]) //上次申请成功,这次不成功
        +dis[pre[i-1]][now[i]]*(1-p[i-1])*p[i] //上次申请不成功,这次成功
        +dis[pre[i-1]][pre[i]]*(1-p[i-1])*(1-p[i]) //两次申请都不成功

    然后我们就对以上两个式子取一下min:

    f[i][j][1]=min(
        f[i-1][j-1][0]+dis[pre[i-1]][now[i]]*p[i]+dis[pre[i-1]][pre[i]]*(1-p[i]),
        f[i-1][j-1][1]+dis[now[i-1]][now[i]]*p[i-1]*p[i]+dis[now[i-1]][pre[i]]*p[i-1]*(1-p[i])+dis[pre[i-1]][now[i]]*(1-p[i-1])*p[i]+dis[pre[i-1]][pre[i]]*(1-p[i-1])*(1-p[i])
    )

    Emmmm这个状态转移方程看上去很简单不是么(大雾)
    边界条件的话,

    f[1][0][0]=f[1][1][1]=0;

    然后一路推过去就行了~~
    代码:

    #include <cstdio>
    const int V = 303;
    const int N = 2002;
    template<class T>
    inline T min(const T &a, const T &b) {
        if (a<b) return a; return b;
    }
    int d[V][V], n, m, v, e;
    double f[N][N][2];
    struct apply {
        double p; int pre, now;
    }a[N];
    
    void floyd() {
        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&&k != j)
                        d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
    }
    inline int getint() {
        int a = 0; char c = getchar(); bool f = 0;
        for (; (c<'0' || c>'9') && c != '-'; c = getchar());
        if (c == '-') f = 1, c = getchar();
        for (; c >= '0'&&c <= '9'; c = getchar()) a = (a << 1) + (a << 3) + c - '0';
        if (f) return -a; return a;
    }
    int main() {
        n = getint(), m = getint(), v = getint(), e = getint(); 
        for (int i = 1; i <= n; i++) a[i].pre = getint();
        for (int i = 1; i <= n; i++) a[i].now = getint();
        for (int i = 1; i <= n; i++) scanf("%lf", &a[i].p);
        for (int i = 0; i <= v; i++) for (int j = 0; j <= v; j++) d[i][j] = 0x3fffffff;
        for (int i = 0; i <= v; i++) d[i][i] = 0;
        for (int i = 1; i <= e; i++) { int x = getint(), y = getint(); d[x][y] = d[y][x] = min(d[x][y], getint()); }floyd();
    
        for (int i = 1; i <= n; i++) for (int j = 0; j <= m; j++) f[i][j][0] = f[i][j][1] = 0x3fffffff;
        f[1][0][0] = f[1][1][1] = 0;
        for (int i = 2; i <= n; i++)
            for (int j = 0; j <= m; j++) {
                f[i][j][0] = min(
                    f[i - 1][j][0] + d[a[i - 1].pre][a[i].pre],
                    f[i - 1][j][1] + d[a[i - 1].now][a[i].pre] * a[i - 1].p + d[a[i - 1].pre][a[i].pre] * (1 - a[i - 1].p)
                );
                if(j) f[i][j][1] = min(
                    f[i - 1][j - 1][0] + d[a[i - 1].pre][a[i].now] * a[i].p + d[a[i - 1].pre][a[i].pre] * (1 - a[i].p),
                    f[i - 1][j - 1][1] + d[a[i - 1].now][a[i].now] * a[i - 1].p * a[i].p
                    + d[a[i - 1].now][a[i].pre] * a[i - 1].p * (1 - a[i].p)
                    + d[a[i - 1].pre][a[i].now] * (1 - a[i - 1].p) * a[i].p
                    + d[a[i - 1].pre][a[i].pre] * (1 - a[i - 1].p) * (1 - a[i].p)
                );
            }
        double ans = 1e10;
        for (int i = 0; i <= m; i++) ans = min(ans, min(f[n][i][0], f[n][i][1])); printf("%.2lf", ans);
    }

    跑得还是挺慢的……Emmmm……

    就这样吧= =

  • 相关阅读:
    服务器上的 Git
    进程有哪几种基本状态,在一个系统中为什么必须区分出这几种状态?
    什么是进程,进程与程序的主要区别是什么?
    什么是与时间有关的错误,是举例说明。
    试解释下列名词:程序的顺序执行,程序的并发执行。
    简述系统调用的执行过程?
    假定某系统提供硬件的访管指令(例如形式:“svc n”),为了实现系统调用,系统设计者应做哪些工作?用户又如如何请求操作系统服务?
    什么是系统调用,对操作系统的服务请求与一般子程序调用有什么区别?
    Windows系统提供什么样的接口,Unix、Linux系统的用户接口是什么?
    用户与操作系统的接口是什么?一个分时系统提供什么接口?一个批处理系统又提供什么接口?
  • 原文地址:https://www.cnblogs.com/enzymii/p/8412137.html
Copyright © 2020-2023  润新知