洛谷P1850 换教室 [noip2016] 期望dp

时间:2021-02-17 21:47:31

正解:期望dp

解题报告:

哇我发现我期望这块真的布星,可能在刷了点儿NOIp之后会去搞一波期望dp的题...感觉连基础都没有打扎实?基础概念都布星!

好那先把这题理顺了嗷qwq

首先我们看到期望就会想到dp是趴,加上dp也确实很NOIp那就直接往dp的方向想嘛

比较容易想到的状态就是f[i][j]表示到第i个阶段了然后已经申请了j次的最小体力,然而在思考转移方程的时候就会发现如果这么设的话好像是不会转移的嗷,因为我们之后的转移会和你上一次是去的c还是d有关,然后就考虑再加一轮[0/1]表示上次申请了没有

然后转移这儿,巨麻烦一点点扣

f[i][j][0]:到第i个阶段了申请了j次这次没申请

既然这次没申请那i-1状态肯定也是申请j次,于是可以知道是从f[i][j]转移来的,这里简单

然后有几种可能呢?

    • 上次没申请,那就是f[i][j][0],此时的概率是100%(不申请百分百通不过嘛),那就是f[i][j][0]+dis[c[i-1]][c[i]]
    • 上次申请了,那就是f[i][j][1],此时可能通过可能没通过,如果通过了+dis[d[i-1]][c[i]]*k[i-1],没通过+dis[c[i-1]][c[i]]*(1-k[i-1])
    • 总结起来就是 min( f[i-1][j][0] + jl[c[i-1]][c[i]] , f[i-1][j][1] + (1-k[i-1])*jl[c[i-1]][c[i]] + k[i-1]*jl[d[i-1]][c[i]])

f[i][j][1]:到第i个阶段了申请了j次这次申请了

理由同上,可以知道是从f[i][j-1]转移来的

这个就会比较长了...

    • 上次没申请,f[i][j-1][0],这时有俩可能,如果这次申请通过了,+dis[c[i-1]][d[i]]*k[i],没通过,+dis[c[i-1]][c[i]]*(1-k[i])
    • 总结起来就是 f[i-1][j-1][0] + k[i]*jl[c[i-1]][d[i]] + (1-k[i])*jl[c[i-1]][c[i]]
    • 上次申请了,f[i][j-1][1],然后有四种可能(上次/这次通过否 因此22
    • 如果上次通过了这次通过了,+dis[d[i-1]][d[i]]*k[i-1]*k[i],如果上次通过了这次没通过,+dis[d[i-1]][c[i]]*k[i-1]*(1-k[i]),如果上次没通过这次通过了,+dis[c[i-1]][d[i]]*(1-k[i-1])*k[i],如果上次没通过这次还是没通过,+dis[c[i-1]][c[i]]*(1-k[i-1])*(1-k[i])
    • 总结起来就是f[i-1][j-1][1] + (1-k[i-1])*(1-k[i])*jl[c[i-1]][c[i]] + (1-k[i-1])*k[i]*jl[c[i-1]][d[i]] + k[i-1]*(1-k[i])*jl[d[i-1]][c[i]] + k[i-1]*k[i]*jl[d[i-1]][d[i]]
    • 然后这整个儿就是 min( f[i-1][j-1][0] + k[i]*jl[c[i-1]][d[i]] + (1-k[i])*jl[c[i-1]][c[i]] , f[i-1][j-1][1] + (1-k[i-1])*(1-k[i])*jl[c[i-1]][c[i]] + (1-k[i-1])*k[i]*jl[c[i-1]][d[i]] + k[i-1]*(1-k[i])*jl[d[i-1]][c[i]] + k[i-1]*k[i]*jl[d[i-1]][d[i]] )

昂然后我觉得这个理顺了就差不多了?最后放个代码就好了qwq

#include<bits/stdc++.h>
using namespace std;
#define rp(i,x,y) for(register int i=x;i<=y;++i)

+,N=+,M=+;
;
int n,m,v,e,c[N],d[N],jl[V][V];
],ans=;

inline int read()
{
    ;;
    '))ch=getchar();
    if(ch=='-')ch=getchar();
    )+(x<<)+(ch^'),ch=getchar();
    return y?x:-x;
}
inline void floyd()
{
    rp(k,,v)
        rp(i,,v)
            rp(j,,i-)jl[j][i]=jl[i][j]=min(jl[i][j],jl[k][i]+jl[j][k]);
}
inline double min3(double x,double y,double z){return min(min(x,y),z);}

int main()
{
    n=read();m=read();v=read();e=read();
    rp(i,,n)c[i]=read();rp(i,,n)d[i]=read();
    rp(i,,n)scanf("%lf",&k[i]);
    rp(i,,v)rp(j,,i-)jl[i][j]=jl[j][i]=(int)inf;
    rp(i,,e){int t1=read(),t2=read(),t3=read();jl[t1][t2]=min(t3,jl[t1][t2]);jl[t2][t1]=jl[t1][t2];}
    floyd();
    rp(i,,n)
        rp(j,,m)f[i][j][]=f[i][j][]=inf;
    f[][][]=f[][][]=;
    rp(i,,n)
    {
        rp(j,,m)
        {
            f[i][j][]=min( f[i-][j][] + jl[c[i-]][c[i]] , f[i-][j][] + (-k[i-])*jl[c[i-]][c[i]] + k[i-]*jl[d[i-]][c[i]]);
            )
            {
                f[i][j][]=min( f[i-][j-][] + k[i]*jl[c[i-]][d[i]] + (-k[i])*jl[c[i-]][c[i]] , f[i-][j-][] + (-k[i-])*(-k[i])*jl[c[i-]][c[i]] + (-k[i-])*k[i]*jl[c[i-]][d[i]] + k[i-]*(-k[i])*jl[d[i-]][c[i]] + k[i-]*k[i]*jl[d[i-]][d[i]] );
            }
        }
    }
    rp(i,,m)ans=min3(ans,f[n][i][],f[n][i][]);
    printf("%.2lf",ans);
}
//哦还有个细节,就,这个题目的初值赋值的时候要注意一下...我开始开小了然后只88,后来开大了点儿又爆了范围直接变成负数...ummm...真实烦skr人QAQ然后还交了两次才过的...