BZOJ 4720 [Noip2016]换教室 期望DP

时间:2021-09-06 14:59:18

题目大意:现有v个点和e条边。在n个时刻中,某个时刻i应该前往c[i],可以申请前往d[i],有p[i]的几率成功,每个时刻只能申请一次,最多申请m次。问如何分配申请使得期望经过的边权最小。
v<=300,n,m<=2000

设状态为f(i,j,k)表示在第i个时刻已经申请了j次,k表示当前时刻是否申请。
分别按照这一时刻申请是否成功和上一时刻申请是否成功讨论。
具体转移见代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 2005
#define M 305
#define INF 100000000
using namespace std;
namespace Graph {
    int n,m,d[M][M];
    void read() {
        memset(d,0x3f,sizeof d);
        for(int i=1;i<=n;++i) d[i][i]=0;
        for(int i=1,x,y,z;i<=m;++i)
            scanf("%d%d%d",&x,&y,&z),
            d[x][y]=d[y][x]=min(d[x][y],z);
        return ;
    }
    void init() {
        for(int k=1;k<=n;++k)
            for(int i=1;i<=n;++i)
                for(int j=1;j<=n;++j)
                    d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
        return ;
    }
}
namespace DP {
    int n,m,a[N],b[N];
    double f[N][N][2],p[N];
    void read() {
        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",p+i);
        return ;
    }
    double solve() {
        using Graph::d;
        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;
        for(int i=2;i<=n;++i) {
            for(int j=0;j<=m;++j) {
                double minn=INF,tmp;
                tmp=f[i-1][j][0];
                tmp+=d[a[i-1]][a[i]];
                minn=min(minn,tmp);
                tmp=f[i-1][j][1];
                tmp+=d[a[i-1]][a[i]]*(1-p[i-1]);
                tmp+=d[b[i-1]][a[i]]*p[i-1];
                minn=min(minn,tmp);
                f[i][j][0]=minn;

                if(!j) continue;
                minn=INF;
                tmp=f[i-1][j-1][0];
                tmp+=d[a[i-1]][b[i]]*p[i];
                tmp+=d[a[i-1]][a[i]]*(1-p[i]);
                minn=min(minn,tmp);
                tmp=f[i-1][j-1][1];
                tmp+=d[a[i-1]][a[i]]*(1-p[i-1])*(1-p[i]);
                tmp+=d[a[i-1]][b[i]]*(1-p[i-1])*p[i];
                tmp+=d[b[i-1]][a[i]]*p[i-1]*(1-p[i]);
                tmp+=d[b[i-1]][b[i]]*p[i-1]*p[i];
                minn=min(minn,tmp);
                f[i][j][1]=minn;
            }
        }
        double ans=INF;
        for(int i=0;i<=m;++i) ans=min(ans,min(f[n][i][0],f[n][i][1]));
        return ans;
    }
}

int main() {
    scanf("%d%d%d%d",&DP::n,&DP::m,&Graph::n,&Graph::m);
    DP::read(), Graph::read();
    Graph::init();
    printf("%.2f\n",DP::solve());
    return 0;
}