【BZOJ4720】【NOIP2016】换教室 [期望DP]

换教室

Time Limit: 20 Sec  Memory Limit: 512 MB
[Submit][Status][Discuss]

Description

Input

  第一行四个整数n,m,v,e。n表示这个学期内的时间段的数量;m表示牛牛最多可以申请更换多少节课程的教室;
  v表示牛牛学校里教室的数量;e表示牛牛的学校里道路的数量。
  第二行n个正整数,第i(1≤i≤n)个正整数表示c,,即第i个时间段牛牛被安排上课的教室;保证1≤ci≤v。
  第三行n个正整数,第i(1≤i≤n)个正整数表示di,即第i个时间段另一间上同样课程的教室;保证1≤di≤v。
  第四行n个实数,第i(1≤i≤n)个实数表示ki,即牛牛申请在第i个时间段更换教室获得通过的概率。保证0≤ki≤1。
  接下来e行,每行三个正整数aj,bj,wj,表示有一条双向道路连接教室aj,bj,通过这条道路需要耗费的体力值是Wj;
  保证通过学校里的道路,从任何一间教室出发,都能到达其他所有的教室。
  保证输入的实数最多包含3位小数。

Output

  输出一行,包含一个实数,四舎五入精确到小数点后恰好2位,表示答案。你的
  输出必须和标准输出完全一样才算正确。

Sample Input

  3 2 3 3
  2 1 2
  1 2 1
  0.8 0.2 0.5
  1 2 5
  1 3 3
  2 3 1

Sample Output

  2.80

HINT

  1≤aj,bj≤v, 1≤wj≤100。
  1≤n≤2000, 0≤m≤2000, 1≤v≤300, 0≤e≤90000。

Main idea

  给定n个 原本教室ci 和 替换教室di,可以申请m次换课,如果 i 换课了则可以在di上课,否则在ci上课,每个教室之间有距离,求期望最小距离。

Solution

  很简单的期望DP,我们令 f[i][j][0\1] 表示 到了第 i 个状态,已经换了 j 次课,这次换不换课,然后分四种情况讨论一下即可。

Code

#include<iostream>    
#include<string>    
#include<algorithm>    
#include<cstdio>    
#include<cstring>    
#include<cstdlib>
#include<cmath>
using namespace std;  
 
#define Road(a,b) (double)w[a][b]
#define tense(a,b) a = a<b ? a:b;
 
const int ONE = 2005;
const double INF = 1e18;
 
int n,m,v,e;
int x,y,z;
int c[ONE],d[ONE];
int w[301][301];
double f[ONE][ONE][2],k[ONE];
double Ans;
 
int get()
{    
        int res=1,Q=1;char c;    
        while( (c=getchar())<48 || c>57 ) 
        if(c=='-')Q=-1; 
        res=c-48;     
        while( (c=getchar())>=48 && c<=57 )    
        res=res*10+c-48;    
        return res*Q;    
}
 
void Floyed()
{
        for(int k=1; k<=v; k++)
        for(int i=1; i<=v; i++)
        for(int j=1; j<=v; j++)
            tense(w[i][j], w[i][k] + w[k][j]);
}
 
double Eap10(int i)
{
        return Road( c[i],c[i+1] ) * (1-k[i]) + Road( d[i],c[i+1] ) * k[i];
}

double Eap01(int i)
{
        return Road( c[i],c[i+1] ) * (1-k[i+1]) + Road( c[i],d[i+1] ) * k[i+1];
}

double Eap11(int i)
{
        return 
            Road( c[i], c[i+1] ) * (1-k[i]) * (1-k[i+1]) 
          + Road( c[i], d[i+1] ) * (1-k[i]) * k[i+1]
          + Road( d[i], c[i+1] ) * k[i] * (1-k[i+1])
          + Road( d[i], d[i+1] ) * k[i] * k[i+1]; 
}

void DisApply(int i,int j)
{ 
        if(j>=0) f[i+1][j][0] = tense(f[i+1][j][0], f[i][j][0] + Road( c[i],c[i+1] ) );
        if(j>=1) f[i+1][j][0] = tense(f[i+1][j][0], f[i][j][1] + Eap10(i) );
}
 
void Apply(int i,int j)
{
        if(j>=1) f[i+1][j][1] = tense(f[i+1][j][1], f[i][j-1][0] + Eap01(i) );
        if(j>=2) f[i+1][j][1] = tense(f[i+1][j][1], f[i][j-1][1] + Eap11(i) );
}

int main()  
{    
        n = get();  m = get();  v = get();  e = get();
        for(int i=1; i<=n; i++) c[i] = get();
        for(int i=1; i<=n; i++) d[i] = get();
        for(int i=1; i<=n; i++) scanf("%lf", &k[i]);
         
        memset(w, 1, sizeof(w));
        for(int i=1; i<=v; i++) w[i][i] = 0;
        for(int i=1; i<=e; i++)
        {
            x = get();  y = get();  z = get();
            w[x][y] = min(w[x][y], z);
            w[y][x] = min(w[y][x], z);
        }
        Floyed();
         
        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=1; i<=n-1; i++)
            for(int j=0; j<=m; j++)
                DisApply(i,j), Apply(i,j);
         
        Ans = INF;
        for(int j=0; j<=m; j++)
        {
            tense(Ans, f[n][j][0]);
            tense(Ans, f[n][j][1]);
        }
         
        printf("%.2lf",Ans);
}
View Code

 

posted @ 2017-05-16 19:53  BearChild  阅读(261)  评论(0编辑  收藏  举报