bzoj 4720/Luogu 1850(期望dp)(NOIP 2016)

传送门
NOIP 2016 D1T3
题意:有n个时间段,第i个时间段可以选择在ci教室上课,也可以选择申请换课,有ki概率申请通过,在di上课,另外1−ki的概率留在ci教室。
总共有v个教室,e条路径双向联通教室xi和yi,路径有权值wi。在课间时(相邻两个时间段的间隔中),你要从上一个教室走最短路径到下一个教室。
现在你有m次申请机会,只能提前申请一堆换课(也就是你不能在知道某一次申请结果后再去申请下一个换课)。求总距离的最小期望。
1≤n≤2000,0≤m≤2000,1≤v≤300,0≤e≤90000,1≤wi≤100,0≤ki≤1

题解:设f[i][j][k]表示前i个时间段,选了j个课程,当前k=0/1表示选/不选的最短路径期望。
然后分四种情况:(0,0),(0,1),(1,0),(1,1)讨论,为什么要讨论前一种情况呢,因为上一次换不换会影响到这次。
P.S.Floyd这种东西打慢点,打快了写错一个字母就是40分钟( ̄工 ̄lll)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int c[2002],d[2002],n,m,v,e;
double k[2002],dis[302][302],ans=0,f[2002][2002][2];
int main() {
    freopen("P1850.in","r",stdin);
    scanf("%d%d%d%d",&n,&m,&v,&e);
    for (int i=1;i<=n;++i) scanf("%d",&c[i]);//original position 
    for (int i=1;i<=n;++i) scanf("%d",&d[i]);//new position
    for (int i=1;i<=n;++i) scanf("%lf",&k[i]);//probability
    memset(dis,127,sizeof(dis));
    for (int i=1;i<=e;++i) {
        int u,v;double w;
        scanf("%d%d%lf",&u,&v,&w);
        dis[u][v]=min(dis[u][v],w);
        dis[v][u]=min(dis[v][u],w);
    }
    for (int i=1;i<=v;++i) dis[i][i]=0;
    for (int s=1;s<=v;++s)//Floyd
        for (int i=1;i<=v;++i)
            for (int j=1;j<=v;++j)
                dis[i][j]=min(dis[i][j],dis[i][s]+dis[s][j]);
    memset(f,127,sizeof(f));
    f[1][0][0]=f[1][1][1]=0;
    for (int i=2;i<=n;++i) {
        int cur=min(i,m);
        for (int j=0;j<=cur;++j) {
            f[i][j][0]=f[i-1][j][0]+dis[c[i-1]][c[i]];
            f[i][j][0]=min(f[i][j][0],f[i-1][j][1]+dis[d[i-1]][c[i]]*k[i-1]+dis[c[i-1]][c[i]]*(1-k[i-1]));
            if (j) {
                f[i][j][1]=f[i-1][j-1][0]+dis[c[i-1]][d[i]]*k[i]+dis[c[i-1]][c[i]]*(1-k[i]);
                f[i][j][1]=min(f[i][j][1],f[i-1][j-1][1]+dis[c[i-1]][c[i]]*(1-k[i-1])*(1-k[i])+dis[c[i-1]][d[i]]*(1-k[i-1])*k[i]+dis[d[i-1]][c[i]]*k[i-1]*(1-k[i])+dis[d[i-1]][d[i]]*k[i-1]*k[i]);
            }
        }
    }//expectation
    ans=1e9+7;
    for (int i=0;i<=m;++i) ans=min(ans,min(f[n][i][0],f[n][i][1]));
    printf("%.2lf\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值