[NOIP2016]换教室

题目大意

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

1n2000,0m2000,1v300,0e90000,1wi100,0ki1


题目分析

这题是很水的期望题。
由期望的线性性

E[X]=i=2nE[dis(i1,i)]

因此我们可以分开来计算每一个课间的期望距离。
fi,j,0/1 表示当前在第 i 个时间段,已经申请了j次,这个时间段是否申请的最小期望。
然后直接枚举上一个课间是否申请,分类讨论各自的概率(申请包括了是否通过)转移就好了。这个很简单,相信大家都懂。
至于最短路,直接上 Floyd
时间复杂度 O(v3+nm)


代码实现

#include <iostream>
#include <climits>
#include <cfloat>
#include <cstdio>
#include <cctype>

using namespace std;

typedef double db;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}

const int INF=INT_MAX/3;
const db FINF=DBL_MAX/3;
const int N=2005;
const int M=2005;
const int V=305;
const int E=90005;

db f[N][M][2];
int dis[V][V];
int c[N],d[N];
int n,m,v,e;
db p[N];
db ans;

void floyd()
{
    for (int k=1;k<=v;k++)
        for (int i=1;i<=v;i++)
            if (i!=k)
                for (int j=1;j<=v;j++)
                    if (i!=j&&j!=k)
                        dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}

void dp()
{
    for (int i=1;i<=n;i++)
        for (int j=0;j<=m;j++)
            f[i][j][0]=f[i][j][1]=FINF;
    f[1][0][0]=f[1][1][1]=0;
    for (int i=2;i<=n;i++)
        for (int j=0;j<=i&&j<=m;j++)
        {
            int dxu=dis[c[i-1]][c[i]],dxv=dis[c[i-1]][d[i]],dyu=dis[d[i-1]][c[i]],dyv=dis[d[i-1]][d[i]];
            db p1=p[i-1],p2=p[i];
            if (j<=i-1) f[i][j][0]=min(p1*dyu+(1-p1)*dxu+f[i-1][j][1],dxu+f[i-1][j][0]);
            if (j) f[i][j][1]=min(f[i-1][j-1][0]+p2*dxv+(1-p2)*dxu,f[i-1][j-1][1]+p1*p2*dyv+p1*(1-p2)*dyu+(1-p1)*p2*dxv+(1-p1)*(1-p2)*dxu);
        }
    ans=FINF;
    for (int j=0;j<=m&&j<=n;j++) ans=min(ans,min(f[n][j][0],f[n][j][1]));
}

int main()
{
    freopen("classroom.in","r",stdin),freopen("classroom.out","w",stdout);
    n=read(),m=read(),v=read(),e=read();
    for (int i=1;i<=n;i++) c[i]=read();
    for (int i=1;i<=n;i++) d[i]=read();
    for (int i=1;i<=n;i++) scanf("%lf",&p[i]);
    for (int i=1;i<=v;i++)
    {
        for (int j=1;j<=v;j++)
            dis[i][j]=INF;
        dis[i][i]=0;
    }
    for (int i=1,x,y,z;i<=e;i++) x=read(),y=read(),z=read(),dis[x][y]=min(dis[x][y],z),dis[y][x]=min(dis[y][x],z);
    floyd(),dp();
    printf("%.2lf\n",ans);
    fclose(stdin),fclose(stdout);
    return 0;
}
  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值