[题解] P1850 换教室 Floyd+DP
洛谷题目链接
牛客题目链接
本题可以先预处理一下任意两个教室之间的距离,可以使用Floyd算法,设dis[i][j]表示教室i到教室j的距离
Floyd算法:dis[i][j]=min(dis[i][k]+dis[k][j])k为i到j中间可能经过的一个节点,代码如下:
void Floyd()
{
for(int k=1;k<=v;k++)
for(int i=1;i<=v;i++)
for(int j=1;j<i;j++)
if(dis[i][j]>dis[i][k]+dis[k][j])
dis[j][i]=dis[i][j]=dis[i][k]+dis[k][j];
}
注意:dis数组初始化为最大值,具体算法证明可以参考:Floyd算法参考
经过Floyd之后,进入正式的Dp环节(这个题的转移过程比较复杂),用f[i][j][2]表示前i节课,已经申请了j次,dii节课有没有申请(f[i][j][0]为未申请,f[i][j][1]为申请),先分两个大部分
1.第i节课没有申请,即f[i][j][0]
- 前一间教室申请:f[i−1][j][1] + k[i−1]∗dis[d[i−1]][c[i]] (成功) + (1−k[i−1])∗dis[c[i−1]][c[i]] (失败)
- 前一间教室没有申请:f[i−1][j][0] + dis[c[i−1]][c[i]])
f[i][j][0]=
min(f[i-1][j][1]+ k[i-1]*dis[d[i-1]][c[i]]+ (1-k[i-1])*dis[c[i-1]][c[i]], //前一间教室申请
f[i-1][j][0] +dis[c[i-1]][c[i]]); //前一间教室不申请
2.第i节课申请,即f[i][j][1]
- 如果前一教室有申请:如果前一教室有申请:f[i−1][j−1][1]
- 前后均成功:k[i-1]*k[i]*dis[d[i-1]][d[i]]+k[i−1]∗k[i]∗dis[d[i−1]][d[i]]
- 前成功后失败:k[i-1]*(1-k[i])*dis[d[i-1]][c[i]]+k[i−1]∗(1−k[i])∗dis[d[i−1]][c[i]]
- 前失败后成功:(1-k[i-1])*k[i]*dis[c[i-1]][d[i]]+(1−k[i−1])∗k[i]∗dis[c[i−1]][d[i]]
- 前后均失败:(1-k[i-1])*(1-k[i])*dis[c[i-1]][c[i]]+(1−k[i−1])∗(1−k[i])∗dis[c[i−1]][c[i]]
- 所有情况加起来便是答案
- 如果前一教室没有申请:f[i-1][j-1][0]
- 后成功:k[i]*dis[c[i-1]][d[i]]+k[i]∗dis[c[i−1]][d[i]]
- 后失败:(1-k[i])*dis[c[i-1]][c[i]])+(1−k[i])∗dis[c[i−1]][c[i]])
- 所有情况加起来便是答案
f[i][j][1]=min(
f[i-1][j-1][1]+ //前一间教室申请
k[i-1]*k[i]*dis[d[i-1]][d[i]]+
k[i-1]*(1-k[i])*dis[d[i-1]][c[i]]+
(1-k[i-1])*k[i]*dis[c[i-1]][d[i]]+
(1-k[i-1])*(1-k[i])*dis[c[i-1]][c[i]],
f[i-1][j-1][0]+ //前一间教室不申请
k[i]*dis[c[i-1]][d[i]]+
(1-k[i])*dis[c[i-1]][c[i]]);
最后所有f[n][i][j]的最大值即是答案
void Print_ans(){
double ans=INF;
for(int i=0;i<=m;i++)
for(int j=0;j<=1;j++)
ans=min(ans,f[n][i][j]);
printf("%.2lf",ans);
}
Floyd的时间复杂度是O(v^3),Dp是O(n^2),可以接受
AC代码:
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=2e3+7,maxv=3e2+7,INF=1e7+7;
int n,m,v,e,a,b,w;
int c[maxn],d[maxn],dis[maxv][maxv];
double k[maxn],f[maxn][maxn][2];
void Read(){ //读入
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<i;j++)
dis[j][i]=dis[i][j]=INF;
for(int i=1;i<=e;i++){
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
dis[a][b]=min(dis[a][b],w);
dis[b][a]=dis[a][b];
}
}
void Floyd(){ //Floyd
for(int k=1;k<=v;k++)
for(int i=1;i<=v;i++)
for(int j=1;j<i;j++)
if(dis[i][j]>dis[i][k]+dis[k][j])
dis[j][i]=dis[i][j]=dis[i][k]+dis[k][j];
}
void Dp(){ //Dp
for (int i=1;i<=n;i++)
for (int j=0;j<=m;j++)
f[i][j][0]=f[i][j][1]=INF;
f[1][0][0]=0;
f[1][1][1]=0;
for(int i=2;i<=n;i++)
for(int j=0;j<=m;j++){
f[i][j][0]= min(f[i-1][j][1]+
k[i-1]*dis[d[i-1]][c[i]]+
(1-k[i-1])*dis[c[i-1]][c[i]],
f[i-1][j][0]+
dis[c[i-1]][c[i]]);
if (j!=0)
f[i][j][1]=min(f[i-1][j-1][1]+
k[i-1]*k[i]*dis[d[i-1]][d[i]]+
k[i-1]*(1-k[i])*dis[d[i-1]][c[i]]+
(1-k[i-1])*k[i]*dis[c[i-1]][d[i]]+
(1-k[i-1])*(1-k[i])*dis[c[i-1]][c[i]],
f[i-1][j-1][0]+
k[i]*dis[c[i-1]][d[i]]+
(1-k[i])*dis[c[i-1]][c[i]]);
}
}
void Print_ans(){ //输出答案
double ans=INF;
for(int i=0;i<=m;i++)
for(int j=0;j<=1;j++)
ans=min(ans,f[n][i][j]);
printf("%.2lf",ans);
}
int main(){
Read();
Floyd();
Dp();
Print_ans();
return 0;
}
希望本篇文章能给大家带来帮助