1. 程式人生 > >【NOIP2016提高】換教室題解——作為概率DP入門題

【NOIP2016提高】換教室題解——作為概率DP入門題

題目:luogu1850.

題目大意:給定v個教室,教室之間有e條無向邊邊,保證連通.現在有n組教室,每組有一個被欽定的教室和一個可以替換的教室,現在給你m次替換教室的機會,以及當你打算替換掉一組教室時成功的概率,讓你求最後依次經過你選擇的教室的期望路徑最小值.

我覺得我的題目大意好像解釋得不是很清晰...

模擬賽考真題的時候由於我沒有寫過概率DP,然後一直調這道題調了3h,導致T2部分分一點都沒拿...

現在突然發現自己列的狀態貌似不太主流,然後寫的方式又細節極多,而且還是在我迷了很久期望的定義的情況寫的...

我們現在來詳解這道題.

首先我在考場列的狀態是f[i][j][k]表示到第i個時段,用掉了j次機會,是否成功換掉了第j組教室的期望.

這個狀態實際上是錯誤的,因為這個狀態它已知是否申請成功,而題目上說是一次性提交的,說明不知道是否申請成功.

然後我們會發現這個東西列出方程後細節比較多,我們換一種轉態.

我們依然設狀態f[i][j][k],但這是k表示的是否申請去換地j組教室.

那麼容易列出方程:

\tiny f[i][j][0]=min(f[i-1][j][0]+dis[c[i-1]][c[i]],f[i-1][j][1]+dis[c[i-1]][c[i]]*(1-k[i-1])+dis[d[i-1]][c[i]]*k[i-1])

\tiny f[i][j][1]=min(f[i-1][j-1][0]+dis[c[i-1]][c[i]]*(1-k[i])+dis[c[i-1]][d[i]]*k[i],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])

當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;
}