1. 程式人生 > >noip 2016 換教室

noip 2016 換教室

概率 期望值 www 一次 turn 提示 c++ 當前 一道

第一個 完全自己想的期望+完全自己做的T3 並且 +1遍AC

Description:

luogu 換教室

Solution:

一看是一道期望題。

再一看,發現,v<=300,n,m<=2000有點意思。

大概復雜度n^2確定。

有一張圖?任意兩點間最短路?300就是floyd的提示嘛!!

預處理floyd確定。

發現,階段比較明顯,第i個階段

一看就是一道期望dp題了。

dp狀態:顯然要記錄第i階段,顯然要知道用了幾個申請。恰好n方開的下。

可以設dp[i][j],表示前i階段,包括這個階段用了j個申請,期望值的最小值。

但是,不知道可能會在哪裏,根本無法轉移。

就設dp[i][j][0/1]表示,前i階段,包括這個階段用了j個申請,這一次有沒有用申請。(僅有沒有,過不過不知道。)

這樣就可以轉移了。

因為,知道了在哪裏。

如果上一次沒有用,就在c[i-1],否則一定概率在d[i-1]一定概率在 c[i-1]

轉移的時候,當前是0,1同理處理一下概率即可。

註意,本質上,dp的轉移思想還是,從上一個狀態選擇一個方案,算出這個轉移的代價,取最小的轉移過來。

所以,min肯定是在外面的。

至於期望的轉移是否正確,就審查一下是否所有的情況概率和是1就好啦~!

Code:

#include<bits/stdc++.h>
using namespace std;
const int N=2000+3;
const int M=300+3;
const int inf=0x3f3f3f3f
; const double big=1000000007.00; int dis[M][M]; int c[N],d[N]; double f[N][N][2]; double p[N]; int n,m,v,e; int main() { scanf("%d%d%d%d",&n,&m,&v,&e); for(int i=1;i<=n;i++) scanf("%d",&c[i]); for(int i=1;i<=n;i++) scanf("%d",&d[i]); for(int i=1;i<=n;i++) scanf("
%lf",&p[i]); int x,y,z; memset(dis,inf,sizeof dis); for(int i=1;i<=e;i++){ scanf("%d%d%d",&x,&y,&z); if(x==y) continue; dis[x][y]=min(dis[x][y],z); dis[y][x]=dis[x][y]; } for(int k=1;k<=v;k++){ dis[k][k]=0; for(int i=1;i<=v;i++) for(int j=1;j<=v;j++){ dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); } } for(int i=1;i<=n;i++){ for(int j=0;j<=m;j++) f[i][j][0]=f[i][j][1]=big; } f[1][0][0]=0.0; f[1][1][1]=0.0; for(int i=2;i<=n;i++){ for(int j=0;j<=min(i,m);j++){ f[i][j][0]=min(f[i-1][j][0]+dis[c[i-1]][c[i]], f[i-1][j][1]+p[i-1]*dis[d[i-1]][c[i]]+(1.00-p[i-1])*dis[c[i-1]][c[i]]); if(j>=1) f[i][j][1]=min( (1.00-p[i])*(f[i-1][j-1][0]+dis[c[i-1]][c[i]])+ p[i]*(f[i-1][j-1][0]+dis[c[i-1]][d[i]]), (1.00-p[i])*(f[i-1][j-1][1]+p[i-1]*dis[d[i-1]][c[i]]+(1.00-p[i-1])*dis[c[i-1]][c[i]])+ p[i]*(f[i-1][j-1][1]+p[i-1]*dis[d[i-1]][d[i]]+(1.00-p[i-1])*dis[c[i-1]][d[i]]) ); } } double ans=big; for(int j=0;j<=m;j++){ ans=min(ans,min(f[n][j][0],f[n][j][1])); } printf("%.2lf",ans); return 0; }

noip 2016 換教室