NOIP2016day1T3 换教室 (BZOJ 4729)

该博客介绍了NOIP2016第一天第三题——换教室问题,强调了理解和运用期望这一概念对于解决此题的关键作用。通过动态规划(DP)的角度分析,定义了f[i][j][0..1]表示上完i节课,使用j次申请,第i次申请花费体力的期望最小值,并给出了floyd算法求解g[i][j],即教室之间的最短距离。题目类型被归类为期望DP,时间复杂度为O(nm),空间复杂度为O(2nm)。博主承认博客内容简单,欢迎高手指正。
摘要由CSDN通过智能技术生成

换教室(classroom)

【题目分析】
其实这道题难点就在于期望这个知识背景比较麻烦(以前NOIP-TG考的并不多),如果多了解一下期望(其中最重要的——期望是线性的)这个概念,这个题其实难度不大。
下面由DP的角度看这题:
可以发现两节课之间走什么样的路仅与这两节课有没有申请以及申请有没有成功有关,所以可以定义:

f[i][j][0..1]:上完i节课,已经用了j次申请,其中第i次已申请的花费体力的期望的最小值
g[i][j]:i号教室到j号教室距离的最小值,

g数组因为n<=300直接上floyd;
f的转移方程……其实知道期望这个概念后转移方程很简单:
这里写图片描述
【题目类型】
期望DP
【复杂度】
时间:O(nm); 空间:O(2nm);

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,t,e,dis[305][305],a[2005],b[2005];
double ans,f[2005][2005][2],p[2005],g[305][305];
inline void readi(int &x){
    x=0; char ch=getchar();
    while ('0'>ch||ch>'9') ch=getchar();
    while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
}
inline void readd(double &x){
    x=0; char ch=getchar();
    while ('0'>ch||ch>'9') ch=getchar();
    while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    if (ch=='.'){
        double f=1; ch=getchar();
        while ('0'<=ch&&ch<='9') {f/=10; x+=(ch-'0')*f; ch=getchar();}
    }
}
void g_foyed(){
    for (int i=1;i<=t;i++)
      for (int j=1;j<=t;j++) g[i][j]=dis[i][j];
    for (int i=1;i<=t;i++) g[i][i]=0.0;
    for (int k=1;k<=t;k++)
      for (int i=1;i<=t;i++)
        if (k!=i)
          for (int j=1;j<=t;j++)
            if (k!=j&&i!=j&&g[i][j]>g[i][k]+g[k][j]) g[i][j]=g[i][k]+g[k][j];
}
void _init()
{
    freopen("classroom.in","r",stdin);
    freopen("classroom.out","w",stdout);
    readi(n); readi(m); readi(t); readi(e);
    for (int i=1;i<=n;i++) readi(a[i]);
    for (int i=1;i<=n;i++) readi(b[i]);
    for (int i=1;i<=n;i++) readd(p[i]);
    memset(dis,63,sizeof(dis));
    for (int i=1;i<=e;i++){
        int x,y,z; readi(x); readi(y); readi(z);
        if (x==y) continue;
        if (z<dis[x][y]) dis[x][y]=z; dis[y][x]=dis[x][y];
    }
    g_foyed();
}
void _solve()
{
    memset(f,127,sizeof(f));
    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]+g[a[i-1]][a[i]];
        for (int j=1;j<=min(i,m);j++){
            f[i][j][0]=min(f[i-1][j][1]+g[b[i-1]][a[i]]*p[i-1]+g[a[i-1]][a[i]]*(1.0-p[i-1]),f[i-1][j][0]+g[a[i-1]][a[i]]);
            f[i][j][1]=min(f[i-1][j-1][0]+p[i]*g[a[i-1]][b[i]]+(1.0-p[i])*g[a[i-1]][a[i]],f[i-1][j-1][1]+p[i-1]*p[i]*g[b[i-1]][b[i]]+(1.0-p[i-1])*p[i]*g[a[i-1]][b[i]]+p[i-1]*(1.0-p[i])*g[b[i-1]][a[i]]+(1.0-p[i-1])*(1.0-p[i])*g[a[i-1]][a[i]]);
        }
    }
    double ans=f[n][0][0];
    for (int i=1;i<=m;i++) ans=min(ans,min(f[n][i][0],f[n][i][1]));
    printf("%.2lf",ans);
}
int main()
{
    _init();
    _solve();
    return 0;
}

PS:
本博客较为简陋,望各位神犇谅解,如有错误,请多指出。

PPS:
Orz zzk Orz zkk Orz zzk 重要的事情说三遍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值