2016 Noip Day1 T3 换教室 【概率DP+Floyd】

题目:
https://vijos.org/p/2005
 
分析:
这个题就是在不断地蒙你,先给普通的DP套了一个假的概率的外壳,再把弗洛伊德藏在教室和体力的关系间。看起来复杂吓死人,实际上并没有牵扯什么优化或是高级算法。
所谓的概率,就是给正常的路径值乘了一个限定值,只不过要注意走什么路这个限定值不同。
我们用f [ i ] [ j ] [ 1 ] 表示考虑前 i 个时间段,已经申请了j 个教室的交换,并且当前教室申请的最小体力消耗。
我们用f [ i ] [ j ] [ 0 ] 表示考虑前 i 个时间段,已经申请了j 个教室的交换,并且当前教室不申请的最小体力消耗。
首先肯定要预处理出来所有教室之间的最短路径,再进行DP,此时分情况讨论;(所有变量名与题目一致)
1、当前教室不打算申请:( A 为原本教室, A'为备份教室,B 同理)
2016   Noip Day1 T3   换教室 【概率DP+Floyed】 - linda_fcj - Captains Honor
 f [ i ] [ j ] [ 0 ] = min( f [ i ] [ j ] [ 0 ] , f [ i - 1 ] [ j ] [ 0 ] + w [ c [ i-1] ] [ c [ i ] ] ) ;
表示前一个教室没有申请;
 f [ i ] [ j ] [ 0 ] = min( f [ i ] [ j ] [ 0 ] , f [ i - 1 ] [ j ] [ 1 ] + w [ d [ i - 1 ] ] [ c [ i ] ] * k [ i - 1 ] + w [ c [ i  - 1 ] ] [c [ i ] ] * (1.0-k [ i-1] ));
绿色表示前一个教室申请了也通过了,蓝色表示没有通过。
2、当前教室打算申请:
2016   Noip Day1 T3   换教室 【概率DP+Floyed】 - linda_fcj - Captains Honor
f [ i ] [ j ] [ 1 ] = min( f [ i ] [ j ] [ 1 ] , f [ i -1 ] [ j-1 ] [ 0 ] + k [ i ] *w[ c [i-1] ] [ d[i] ]+(1.0-k [ i ])*w [ c[i-1] ] [ c[i] ]); //前一个不申请
f [ i ] [ j ] [ 1 ] = min ( f [ i ] [ j ] [ 1 ] , f [ i -1 ] [ j-1 ] [ 1 ]
                                                 +k [ i-1 ] * k [ i ] * w [ d[i-1] ] [ d[i] ]          //     前一个选上了,后一个也选上了
                                                 +k [ i-1 ] * (1.0-k[i]) * w [ d[i-1] ] [ c[i] ]    //      前一个选上了,后一个没选上
                                                 +(1.0 - k[i-1]) * k [ i ] * w [ c[i-1] ] [ d[i] ]  //      前一个没选上,后一个选上了
                                                 +(1.0 - k [ i-1 ] ) * ( 1.0-k[i] ) * w [ c[i-1] ] [ c[i] ] );//前一个没选上,后一个也没选上
下面是参考代码:
 
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define inf 1000000007
using namespace std;
int n,m,v,e,a,b,ww;
int c[2020],d[2020],w[330][330];
double k[2020],f[2020][2020][3],ans=1.0*1e9;
int main()
{
 cin>>n>>m>>v>>e;
 for(int i=1;i<=n;i++)
  cin>>c[i];
 for(int i=1;i<=n;i++)
  cin>>d[i];
 for(int i=1;i<=n;i++)
  cin>>k[i];
 for(int i=1;i<=v+10;i++)
  for(int j=0;j<=v+10;j++)
   w[i][j]=1e9;
 for(int i=0;i<=304;i++) w[i][i]=0;
 for(int i=1;i<=e;i++)
 {
  cin>>a>>b>>ww;
  w[a][b]=w[b][a]=min(ww,w[a][b]);
 }
 for(int kk=1;kk<=v;kk++)
  for(int i=1;i<=v;i++)
   for(int j=1;j<=v;j++)
    if(w[i][kk]+w[kk][j]<w[i][j])
     w[i][j]=w[i][kk]+w[kk][j];
 for(int i=1;i<=n;i++)
  for(int j=0;j<=m;j++)
   f[i][j][0]=f[i][j][1]=1.0*1e9;
 f[1][0][0]=f[1][1][1]=0;
 for(int i=2;i<=n;i++)
  for(int j=0;j<=min(i,m);j++)  
  {
   f[i][j][0]=min(f[i][j][0],f[i-1][j][0]+w[c[i-1]][c[i]]);
   f[i][j][0]=min(f[i][j][0],f[i-1][j][1]+w[d[i-1]][c[i]]*k[i-1]+w[c[i-1]][c[i]]*(1.0-k[i-1]));
   if(j>0)
   {
    f[i][j][1]=min(f[i][j][1],f[i-1][j-1][0]+k[i]*w[c[i-1]][d[i]]+(1.0-k[i])*w[c[i-1]][c[i]]);
    f[i][j][1]=min(f[i][j][1],f[i-1][j-1][1]
                                          +k[i-1]*k[i]*w[d[i-1]][d[i]]
                                          +k[i-1]*(1.0-k[i])*w[d[i-1]][c[i]]
               +(1.0-k[i-1])*k[i]*w[c[i-1]][d[i]]
               +(1.0-k[i-1])*(1.0-k[i])*w[c[i-1]][c[i]]);
   }
  }
 for(int i=0;i<=m;i++)
  ans=min(min(ans,f[n][i][1]),f[n][i][0]);
 printf("%.2lf",ans);
 return 0;
} 
View Code

 

posted @ 2017-07-19 16:09  Captain_fcj  阅读(183)  评论(0编辑  收藏  举报