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 同理)
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] ));
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、当前教室打算申请:
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] ] );//前一个没选上,后一个也没选上
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; }