这道题有意思的地方是,他要求提前决策,不能实时决策,这个意思是,我们不能记录当前点是否成功的状态,而只能记录是否申请的状态。
这道题比较明显的状态是f[][][],前i个,申请了j次,第i个有无申请。
但是我想通过记忆化搜索来做,因此将这个状态设计为前i-1个,申请了j次,第i-1个有无申请。这样初始状态就只有一种1,0,0,表示0的位置,申请0次,第0个没申请
因为我们根本没有第0个,所以只有他是合法的。
问的是最短路,只要floyd跑一下就行
在记忆化的时候,有很多情况
当i-1个没申请,那么我们i个可以申请或不申请
当i-1个申请了,同理。
当两个都申请了的话,就有4种情况,都没申请就是1种,其他都是两种
注意当已经申请等于m次,就不能再申请,因此要特判,细节比较多。一般floyd就用邻接矩阵存,注意初始化
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<map> #include<string> #include<vector> using namespace std; const int N=2010; int n,m,v,e; int g[N][N]; int pos[N][2]; double p[N]; double f[N][N][2]; void floyd(){ int k,i,j; for(i=1;i<=v;i++) g[i][i]=0; for(k=1;k<=v;k++){ for(i=1;i<=v;i++){ for(j=1;j<=v;j++) g[i][j]=min(g[i][j],g[i][k]+g[k][j]); } } for(int j=0;j<=v;j++) g[0][j] = 0; } double dfs(int i,int j,int flag){ if(i==n+1) return 0; auto &x=f[i][j][flag]; if(x!=-1) return x; double ans1=0; double ans0=0; if(flag==0) ans0 = dfs(i+1, j, 0) + g[pos[i-1][0]][pos[i][0]]; else ans0 = dfs(i+1, j, 0) + p[i-1]*g[pos[i-1][1]][pos[i][0]] + (1-p[i-1])*g[pos[i-1][0]][pos[i][0]]; if(j==m) return x= ans0; if(!flag){ ans1=p[i]*(dfs(i+1,j+1,1)+g[pos[i-1][0]][pos[i][1]])+(1-p[i])*(dfs(i+1,j+1,1)+g[pos[i-1][0]][pos[i][0]]); } else{ ans1=p[i]*(dfs(i+1,j+1,1)+p[i-1]*(g[pos[i-1][1]][pos[i][1]])+(1-p[i-1])*(g[pos[i-1][0]][pos[i][1]]))+ (1-p[i])*(dfs(i+1,j+1,1)+p[i-1]*(g[pos[i-1][1]][pos[i][0]])+(1-p[i-1])*(g[pos[i-1][0]][pos[i][0]])); } return x=min(ans1,ans0); } int main(){ cin>>n>>m>>v>>e; int i; for(i=1;i<=n;i++) cin>>pos[i][0]; for(i=1;i<=n;i++) cin>>pos[i][1]; for(i=1;i<=n;i++) cin>>p[i]; memset(g,1,sizeof g); for(i=1;i<=e;i++){ int a,b,w; cin>>a>>b>>w; g[a][b]=g[b][a]=min(w,g[a][b]); } floyd(); for(i=1;i<=n;i++){ for(int j=0;j<=m;j++){ f[i][j][0]=f[i][j][1]=-1; } } double ans=dfs(1,0,0); printf("%.2f ",ans); return 0; }