【noip2016】换教室 题解

题目大意

       给出一幅 v 个点的无向图,表示教室及其连边。
       有 n 个时刻,每个时刻正常要到教室 c[i] 上课,如果该时刻有申请更换,则到教室 d[i] 上课。
       你只能在一切开始之前提交申请,且最多申请换 m 个时刻。第 i 个时刻申请成功的概率为 k[i]。
       求移动路程的期望最小值。
       n,m<=2000, v<=300

对于那副图的处理

       Floyd求出任意两点最短路,然后那幅图就没用了。

dp

       一开始我设的是 f[i][j][0/1] 表示,到第 i 个时刻,已经申请了 j 次,第 i 个时刻是否换教室,的最小期望。
       这样推一推就发现是错的了。因为申请与否跟最终换不换教室是两回事。

       所以应该设 f[i][j][0/1]表示,到第 i 个时刻,已经申请了 j 次,第 i 个时刻是否申请,的最小期望。
       然后这样就相当于 i-1 的 0 和 1 两个点,转移到 i 的 0 和 1 两个点。总共四种转移,每种转移的期望代价就考虑一下多大概率是从 c[i-1] 来、多大概率从 d[i-1] 来,多大概率到 c[i] 去、多大概率到 d[i] 去,然后相应地乘上最短路即可。

告诉我v、n打反的不止我一个

       T^T
       QAQ

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

typedef long long LL;

const int maxn=2005, maxv=305;
const LL inf=1e11;

int n,m,v,e,rm[maxn][2];
double k[maxn];

int dis[maxv][maxv];
void Floyd()
{
    fo(k,1,v)
        fo(i,1,v) if (i!=k && dis[i][k]<dis[0][0])
            fo(j,1,v) if (j!=i && j!=k && dis[k][j]<dis[0][0] && dis[i][j]>dis[i][k]+dis[k][j])
                dis[i][j]=dis[i][k]+dis[k][j];
}

double Min(double x,double y)
{
    if (x>inf && y>inf) return inf;
    if (x>inf) return y;
    if (y>inf) return x;
    if (x<y) return x; else return y;
}

double f[maxn][maxn][2];
int main()
{
    freopen("classroom.in","r",stdin);
    freopen("classroom.out","w",stdout);

    scanf("%d %d %d %d",&n,&m,&v,&e);
    fo(i,1,n) scanf("%d",&rm[i][0]);
    fo(i,1,n) scanf("%d",&rm[i][1]);
    fo(i,1,n) scanf("%lf",&k[i]);

    memset(dis,127,sizeof(dis));
    fo(i,1,e)
    {
        int x,y,w;
        scanf("%d %d %d",&x,&y,&w);
        dis[x][y]=min(dis[x][y],w);
        dis[y][x]=min(dis[y][x],w);
    }
    fo(i,1,v) dis[i][i]=0;
    Floyd();

    fo(i,1,n)
        fo(j,0,m)
            fo(c,0,1) f[i][j][c]=inf;
    f[1][0][0]=f[1][1][1]=0;
    fo(i,2,n)
        fo(j,0,m)
            fo(c,0,1)
            {
                double t=k[i-1]*dis[rm[i-1][1]][rm[i][0]]+(1-k[i-1])*dis[rm[i-1][0]][rm[i][0]];
                f[i][j][0]=Min(f[i-1][j][0]+dis[rm[i-1][0]][rm[i][0]],f[i-1][j][1]+t);
                if (j)
                {
                    double t1=k[i]*dis[rm[i-1][0]][rm[i][1]]+(1-k[i])*dis[rm[i-1][0]][rm[i][0]];
                    t=k[i-1]*k[i]*dis[rm[i-1][1]][rm[i][1]]+(1-k[i-1])*k[i]*dis[rm[i-1][0]][rm[i][1]];
                    t+=k[i-1]*(1-k[i])*dis[rm[i-1][1]][rm[i][0]]+(1-k[i-1])*(1-k[i])*dis[rm[i-1][0]][rm[i][0]];
                    f[i][j][1]=Min(f[i-1][j-1][0]+t1,f[i-1][j-1][1]+t);
                }
            }

    double ans=inf;
    fo(j,0,m) ans=min(ans,Min(f[n][j][0],f[n][j][1]));

    printf("%.2f\n",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值