[Noip2016]換教室
[Noip2016]換教室
一.前言
阿巴阿巴,好長的題面……題目連結
二.思路
看到數學期望就基本知道是DP了,然後狀態也比較好想,屬於最基礎的那種 \(f_{ij}\) 表示前 i 個共提交了 j 次申請,但是涉及到申請的概率會交叉的問題,於是再加一維 0/1 表示這次是否嘗試。
這裡多嘴一句,一開始我還以為是總共只能成功 m 次,就把方程設成了 j 次成功……然後後面 0/1 那一維也是不可以設成成功的。(可以自己試試,反正我不行)
然後開始轉移,對於當前情況,有(dis 表示教室之間的距離)
\[f[i][j][0]=\text{min}(dp[i-1][j][0]+dis[c[i-1]][c[i]],\\ dp[i-1][j][1]+(1.0-k[i-1])*dis[c[i-1]][c[i]]+k[i-1]*dis[d[i-1]][c[i]]) \]
就稍微解釋……這次沒有嘗試,則上一次就要嘗試 j 次,然後由上次沒嘗試和嘗試了的成功與否轉移。同理,
\[dp[i][j][1]=\min( dp[i-1][j-1][0]+k[i]*dis[c[i-1]][d[i]]+(1.0-k[i])*dis[c[i-1]][c[i]],\\ dp[i-1][j-1][1]+ k[i-1]*k[i]*dis[d[i-1]][d[i]]+ (1.0-k[i-1])*k[i]*dis[c[i-1]][d[i]]+\\ k[i-1]*(1.0-k[i])*dis[d[i-1]][c[i]]+ (1.0-k[i-1])*(1.0-k[i])*dis[c[i-1]][c[i]] ) \]
感覺有點過於長了……跟上面相同的思想我就不講了。
最後是關於 dis ,看資料範圍,教室數小於300 ,Floyd蕪湖起飛~ (鬼知道我白給一個小時打了個什麼奇怪演算法呢~)
就醬。
三.CODE
#include<iostream> #include<cstdio> #include<algorithm> #include<fstream> #include<cmath> #include<cstring> using namespace std; #define m_for(i,a,b) for(int i=a;i<=b;++i) int read(){ char ch=getchar(); int res=0,f=1; for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1; for(;ch>='0'&&ch<='9';ch=getchar())res=res*10+(ch-'0'); return res*f; } const int MAXN=90005; int n,m,v,e; int c[MAXN],d[MAXN],dis[305][305]; double p[2005][2005][2],dp[2005][2005][2]; double k[MAXN]; int main(){ memset(dis,127/3,sizeof(dis)); n=read();m=read();v=read();e=read(); m=min(m,n); m_for(i,1,n)c[i]=read(); m_for(i,1,n)d[i]=read(); m_for(i,1,n)scanf("%lf",&k[i]); m_for(i,1,v)dis[i][0]=dis[0][i]=dis[i][i]=0.0; for(int i=1,a,b,w;i<=e;++i){ a=read();b=read();w=read(); dis[a][b]=dis[b][a]=min(dis[b][a],w); } m_for(a,1,v)m_for(b,1,v)m_for(w,1,v) dis[b][w]=min(dis[b][w],dis[b][a]+dis[a][w]); m_for(i,1,n)m_for(j,0,m)dp[i][j][0]=dp[i][j][1]=MAXN*100.0; dp[1][1][1]=dp[1][0][0]=0.0; m_for(i,2,n){ dp[i][0][0]=dp[i-1][0][0]+dis[c[i-1]][c[i]]; for(int j=1;j<=i&&j<=m;++j){ dp[i][j][1]=min( dp[i-1][j-1][0]+k[i]*dis[c[i-1]][d[i]]+(1.0-k[i])*dis[c[i-1]][c[i]], dp[i-1][j-1][1]+ k[i-1]*k[i]*dis[d[i-1]][d[i]]+ (1.0-k[i-1])*k[i]*dis[c[i-1]][d[i]]+ k[i-1]*(1.0-k[i])*dis[d[i-1]][c[i]]+ (1.0-k[i-1])*(1.0-k[i])*dis[c[i-1]][c[i]] ); dp[i][j][0]=min( dp[i-1][j][0]+dis[c[i-1]][c[i]], dp[i-1][j][1]+(1.0-k[i-1])*dis[c[i-1]][c[i]]+k[i-1]*dis[d[i-1]][c[i]] ); } } double ans=MAXN*100.0; for(int i=0;i<=m;++i){ ans=min(ans,min(dp[n][i][0],dp[n][i][1])); } printf("%.2lf",ans); return 0; }