【NOIP2016提高组】 Day1 T3 换教室
题目链接:https://www.luogu.org/problemnew/show/P1850
此题正解为dp。
我们先用floyd处理出任意两个教室之间的距离,用dis[i][j]表示。
用f[i][j][0..1]表示在前i个课程中,用了j次换课的机会,第i节课选择换还是不换。
f[i][j][0]可以选择用f[i−1][j][0]更新(即第i−1节课选择不换课),代价为dis[c[i−1]][c[i]]。
同时,第i−1节课也可以选择换课,若换课成功,代价为dis[d[i−1]][c[i]],概率为k[i−1]。若换课不成功,则代价还是dis[c[i−1]][c[i]],概率为(1−k[i−1])。
则f[i][j][0]=min(f[i−1][j][0]+dis[c[i−1]][c[i]],(f[i−1][j−1][1]+dis[d[i−1]][c[i]])∗k[i−1]+(f[i−1][j][0]+dis[c[i−1]][c[i]])∗(1−k));
同理,f[i][j][1]可以选择用f[i−1][j][0]更新,若换课成功,代价为dis[c[i−1]][d[i]],概率为k[i]。若换课不成功,则代价是dis[c[i−1]][c[i]],概率为(1−k[i])。
同时,f[i][j][1]也可以选择用f[i−1][j][1]更新。
若两次换课均成功,代价为dis[d[i−1]][d[i]],概率为k[i]∗k[i−1]。
若两次换课均不成功,代价为dis[c[i−1]][c[i]],概率为(1−k[i])∗(1−k[i−1])。
若第i次成功而第i−1次不成功,代价为dis[c[i−1]][d[i]],概率为(1−k[i−1])∗k[i]。
若第i次不成功而第i−1次成功,代价为dis[d[i−1]][c[i]],概率为k[i−1]∗(1−k[i])。
则f[i][j][1]=min((f[i−1][j][0]+dis[c[i−1]][d[i]])∗k[i]+(f[i−1][j][0]+dis[c[i−1]][c[i]])∗(1−k[i]),f[i−1][j][1]+dis[d[i−1]][d[i]]∗k[i]∗k[i−1]+dis[c[i−1]][c[i]]∗(1−k[i])∗(1−k[i−1])+dis[c[i−1]][d[i]]∗(1−k[i−1])∗k[i]+dis[d[i−1]][c[i]]∗k[i−1]∗(1−k[i]))。
可以看出每次转移是O(1)的,则时间复杂度为O(n∗m)+O(v3)。
最终结果为min(f[n][i][k])i∈[0,m],j∈[0,1]。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define M 2002 5 using namespace std; 6 int n,m,v,e,c[M]={0},d[M]={0}; 7 double p[M]={0},dis[305][305]={0},f[M][M][2]={0},minn=123456789; 8 void pmin(double &x,double y){ 9 x=min(x,y); 10 } 11 12 int main(){ 13 scanf("%d%d%d%d",&n,&m,&v,&e); 14 for(int i=1;i<=n;i++) scanf("%d",c+i); 15 for(int i=1;i<=n;i++) scanf("%d",d+i); 16 for(int i=1;i<=n;i++) scanf("%lf",p+i); 17 for(int i=1;i<=v;i++) 18 for(int j=1;j<=v;j++) dis[i][j]=12345678; 19 for(int i=1;i<=e;i++){ 20 int x,y,z;scanf("%d%d%d",&x,&y,&z); 21 pmin(dis[x][y],z); pmin(dis[y][x],z); 22 } 23 for(int i=1;i<=v;i++) dis[i][i]=0; 24 for(int k=1;k<=v;k++) 25 for(int i=1;i<=v;i++) 26 for(int j=1;j<=v;j++) 27 dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); 28 for(int i=0;i<=n;i++) 29 for(int j=0;j<=m;j++) f[i][j][0]=f[i][j][1]=12345678; 30 f[1][0][0]=0; f[1][1][1]=0; 31 for(int i=1;i<n;i++) 32 for(int j=0;j<=min(i,m);j++){ 33 pmin(f[i+1][j][0],f[i][j][0]+dis[c[i]][c[i+1]]); 34 double px;px=dis[c[i]][d[i+1]]*p[i+1]+dis[c[i]][c[i+1]]*(1-p[i+1]); 35 pmin(f[i+1][j+1][1],f[i][j][0]+px); 36 px=dis[d[i]][c[i+1]]*p[i]+dis[c[i]][c[i+1]]*(1-p[i]); 37 pmin(f[i+1][j][0],f[i][j][1]+px); 38 px=dis[d[i]][d[i+1]]*p[i]*p[i+1]+dis[d[i]][c[i+1]]*p[i]*(1-p[i+1])+dis[c[i]][d[i+1]]*(1-p[i])*p[i+1]+dis[c[i]][c[i+1]]*(1-p[i])*(1-p[i+1]); 39 pmin(f[i+1][j+1][1],f[i][j][1]+px); 40 } 41 42 for(int i=0;i<=m;i++){ 43 minn=min(minn,f[n][i][0]); 44 minn=min(minn,f[n][i][1]); 45 } 46 printf("%.2lf\n",minn); 47 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!