bzoj4720 / P1850 换教室(Floyd+期望dp)

P1850 换教室

先用Floyd把最短路处理一遍,接下来就是重头戏了

用 f [ i ][ j ][ 0/1 ] 表示在第 i 个时间段,发出了 j 次申请(注意不一定成功),并且在这个时间段是否(1/0)申请换了教室

需要知道的一点是:既然是期望,我们求的就是边权*概率(P4316 绿豆蛙的归宿)的和

同样的,在这题中,我们需要求的是,可转移的状态(d [ i ][ j ])*概率之和

既然是求最小期望,那么我们只要求从第 1->n 个时间段中可以转移的最短(期望)路径,统计这条路径上的期望即可

所以列出状态转移方程(详见代码),解决qwq

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
using namespace std;
inline int Int(){
    char c=getchar(); int x=0;
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x;
}
inline int min1(const int &a,const int &b) {return a<b? a:b;}
inline double min2(const double &a,const double &b) {return a<b ?a:b;}

int n,m,v,e,a[2002],b[2002],d[302][302];
double c[2002],f[2002][2002][2];

int main(){
    n=Int(); m=Int(); v=Int(); e=Int();
    for(int i=1;i<=n;++i) a[i]=Int();
    for(int i=1;i<=n;++i) b[i]=Int();
    for(int i=1;i<=n;++i) scanf("%lf",&c[i]);

    memset(d,63,sizeof(d));
    int q1,q2,q3;
    for(int i=1;i<=e;++i){
        q1=Int(); q2=Int(); q3=Int();
        d[q1][q2]=d[q2][q1]= d[q1][q2]<q3 ? d[q1][q2]:q3;
    }
    for(int k=1;k<=v;++k)
        for(int i=1;i<=v;++i)
            for(int j=1;j<=v;++j)
                d[i][j]=min1(d[i][j],d[i][k]+d[k][j]);
    for(int i=1;i<=v;++i) d[i][i]=0; 
//Floyd最短路
for(int i=1;i<=n;++i) for(int j=0;j<=m;++j) f[i][j][0]=f[i][j][1]=100000000; f[1][0][0]=f[1][1][1]=0; for(int i=2;i<=n;++i) //对于每一种状态进行转移:找到一种期望最小、可以转移过来的状态 { f[i][0][0]=f[i-1][0][0]+d[a[i-1]][a[i]]; for(int j=1;j<=m;++j) { f[i][j][0]=min2(f[i][j][0],f[i-1][j][0] +(double)d[a[i-1]][a[i]]); //这次和上次都不申请 f[i][j][0]=min2(f[i][j][0],f[i-1][j][1] +(double)d[b[i-1]][a[i]]*c[i-1] //这次不申请,上次申请成功 +(double)d[a[i-1]][a[i]]*(1.0-c[i-1])); //这次不申请,上次申请失败 f[i][j][1]=min2(f[i][j][1],f[i-1][j-1][0] +(double)d[a[i-1]][b[i]]*c[i] //这次申请成功,上次不申请 +(double)d[a[i-1]][a[i]]*(1.0-c[i])); //这次申请失败,上次不申请 f[i][j][1]=min2(f[i][j][1],f[i-1][j-1][1] +(double)d[b[i-1]][b[i]]*c[i-1]*c[i] //这次和上次都申请成功 +(double)d[a[i-1]][b[i]]*(1.0-c[i-1])*c[i] //这次申请成功,上次申请失败 +(double)d[b[i-1]][a[i]]*c[i-1]*(1.0-c[i]) //这次申请失败,上次申请成功 +(double)d[a[i-1]][a[i]]*(1.0-c[i-1])*(1.0-c[i])); //这次和上次都申请失败 } } double ans=100000000.00; for(int i=0;i<=m;++i) ans=min2(ans,min2(f[n][i][0],f[n][i][1])); //扫一遍找最小期望 printf("%.2lf",ans); return 0; }

 

posted @ 2018-08-27 16:54  kafuuchino  阅读(157)  评论(0编辑  收藏  举报