【NOIP2016提高】換教室題解——作為概率DP入門題
阿新 • • 發佈:2018-11-10
題目:luogu1850.
題目大意:給定v個教室,教室之間有e條無向邊邊,保證連通.現在有n組教室,每組有一個被欽定的教室和一個可以替換的教室,現在給你m次替換教室的機會,以及當你打算替換掉一組教室時成功的概率,讓你求最後依次經過你選擇的教室的期望路徑最小值.
我覺得我的題目大意好像解釋得不是很清晰...
模擬賽考真題的時候由於我沒有寫過概率DP,然後一直調這道題調了3h,導致T2部分分一點都沒拿...
現在突然發現自己列的狀態貌似不太主流,然後寫的方式又細節極多,而且還是在我迷了很久期望的定義的情況寫的...
我們現在來詳解這道題.
首先我在考場列的狀態是f[i][j][k]表示到第i個時段,用掉了j次機會,是否成功換掉了第j組教室的期望.
這個狀態實際上是錯誤的,因為這個狀態它已知是否申請成功,而題目上說是一次性提交的,說明不知道是否申請成功.
然後我們會發現這個東西列出方程後細節比較多,我們換一種轉態.
我們依然設狀態f[i][j][k],但這是k表示的是否申請去換地j組教室.
那麼容易列出方程:
當i=j=0時是要特殊處理的,j>0時f[i][j][1]時沒有意義的,所以要判斷.
程式碼如下:
#include<bits/stdc++.h> using namespace std; #define Abigail inline void typedef long long LL; const int N=2000,V=300,INF=1<<29; int dis[V+9][V+9]; int c[N+9],d[N+9]; double k[N+9],f[N+9][N+9][2],ans=INF*1.0; int n,m,v,e; void floyd(){ for (int k=1;k<=v;k++) 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]); } Abigail into(){ 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",&k[i]); for (int i=1;i<=v;i++) for (int j=1;j<=v;j++) if (i^j) dis[i][j]=INF; int x,y,z; for (int i=1;i<=e;i++){ scanf("%d%d%d",&x,&y,&z); if (dis[x][y]<=z) continue; dis[x][y]=dis[y][x]=z; } } Abigail work(){ floyd(); for (int i=0;i<=n;i++) for (int j=0;j<=m;j++) f[i][j][0]=f[i][j][1]=INF*1.0; f[1][0][0]=0;f[1][1][1]=0; for (int i=2;i<=n;i++){ f[i][0][0]=f[i-1][0][0]+dis[c[i-1]][c[i]]; for (int j=1;j<=m;j++){ f[i][j][0]=min(f[i][j][0],f[i-1][j][0]+dis[c[i-1]][c[i]]); f[i][j][0]=min(f[i][j][0],f[i-1][j][1]+dis[c[i-1]][c[i]]*(1-k[i-1])+dis[d[i-1]][c[i]]*k[i-1]); if (j>0) f[i][j][1]=min(f[i][j][1],f[i-1][j-1][0]+dis[c[i-1]][c[i]]*(1-k[i])+dis[c[i-1]][d[i]]*k[i]); if (j>0) f[i][j][1]=min(f[i][j][1],f[i-1][j-1][1]+dis[c[i-1]][c[i]]*(1-k[i-1])*(1-k[i])+dis[c[i-1]][d[i]]*(1-k[i-1])*k[i]+dis[d[i-1]][c[i]]*k[i-1]*(1-k[i])+dis[d[i-1]][d[i]]*k[i-1]*k[i]); } } for (int i=0;i<=m;i++) ans=min(ans,min(f[n][i][0],f[n][i][1])); } Abigail outo(){ printf("%.2lf\n",ans); } int main(){ into(); work(); outo(); return 0; }