感觉这道题代码细节令人智熄
D1T3换教室
做过且仅做过的一道期望DP,感觉DP不是很难写,就是细节贼多还容易弄混(论36pts -> 100pts的惨痛经历)
思路:
设
d
p
[
i
]
[
j
]
[
0
/
1
]
dp[i][j][0/1]
dp[i][j][0/1]表示:处理到了序列第
i
i
i个元素,使用了
j
j
j次申请机会,当前序列申请还是不申请时(0:申请,1:不申请),按照题意走过的最小期望长度。
于是可以简单的设计出下列转移方程:
d
p
[
i
]
[
j
]
[
0
]
=
m
i
n
(
d
p
[
i
]
[
j
]
[
0
]
,
m
i
n
(
d
p
[
i
−
1
]
[
j
]
[
0
]
+
v
a
l
(
i
,
i
−
1
,
0
,
0
)
,
d
p
[
i
−
1
]
[
j
]
[
1
]
+
v
a
l
(
i
,
i
−
1
,
0
,
1
)
)
)
;
dp[i][j][0]=min(dp[i][j][0],min(dp[i-1][j][0]+val(i,i-1,0,0),dp[i-1][j][1]+val(i,i-1,0,1)));
dp[i][j][0]=min(dp[i][j][0],min(dp[i−1][j][0]+val(i,i−1,0,0),dp[i−1][j][1]+val(i,i−1,0,1)));
d
p
[
i
]
[
j
]
[
1
]
=
m
i
n
(
d
p
[
i
]
[
j
]
[
1
]
,
m
i
n
(
d
p
[
i
−
1
]
[
j
−
1
]
[
0
]
+
v
a
l
(
i
,
i
−
1
,
1
,
0
)
,
d
p
[
i
−
1
]
[
j
−
1
]
[
1
]
+
v
a
l
(
i
,
i
−
1
,
1
,
1
)
)
)
;
dp[i][j][1]=min(dp[i][j][1],min(dp[i-1][j-1][0]+val(i,i-1,1,0),dp[i-1][j-1][1]+val(i,i-1,1,1)));
dp[i][j][1]=min(dp[i][j][1],min(dp[i−1][j−1][0]+val(i,i−1,1,0),dp[i−1][j−1][1]+val(i,i−1,1,1)));
其中
v
a
l
(
i
,
i
−
1
,
c
1
,
c
2
)
val(i,i-1,c1,c2)
val(i,i−1,c1,c2)表示:当前处理到第
i
i
i个节点,考虑当前节点以及它前一个节点是否被申请。
(
0
:
申
请
,
1
:
不
申
请
;
c
1
:
当
前
节
点
,
c
2
:
前
一
个
节
点
)
(0:申请,1:不申请;c1:当前节点,c2:前一个节点)
(0:申请,1:不申请;c1:当前节点,c2:前一个节点)
代码:
注意
d
p
dp
dp的初始化,
f
l
o
y
d
floyd
floyd中
v
v
v才是节点数,要建反向边
#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int N=2050;
const double inf=1e17;
int n,m,v,e,c[N],d[N],dis[N][N];
double dp[N][N][2];
double k[N];
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]);}
double val(int x,int y,int c1,int c2){//c1第i个位置 c2第i-1个位置
double ans=0;
if(c1==0&&c2==0){//原来两个教室之间的路径期望长度
return dis[c[x]][c[y]];
}
else if(c1==0&&c2==1){//对这个申请成功/不成功的概率*成功/不成功的路径长度,求和
ans+=k[y]*dis[d[y]][c[x]];
ans+=(1-k[y])*dis[c[x]][c[y]];
return ans;
}
else if(c1==1&&c2==0){//对这个申请成功/不成功的概率*成功/不成功的路径长度,求和
ans+=k[x]*dis[d[x]][c[y]];
ans+=(1-k[x])*dis[c[x]][c[y]];
return ans;
}
else if(c1==1&&c2==1){//四种情况分别按概率加权求和
ans+=k[x]*k[y]*dis[d[x]][d[y]];
ans+=k[x]*(1-k[y])*dis[d[x]][c[y]];
ans+=(1-k[x])*(k[y])*dis[c[x]][d[y]];
ans+=(1-k[x])*(1-k[y])*dis[c[x]][c[y]];
return ans;
}
}
inline int read(){
int cnt=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c^48);c=getchar();}
return cnt*f;
}
int main(){
n=read(),m=read(),v=read(),e=read();memset(dis,0x3f,sizeof(dis));
for(int i=1;i<=n;i++)c[i]=read();
for(int i=1;i<=n;i++)d[i]=read();
for(int i=1;i<=n;i++)scanf("%lf",&k[i]);
int x,y,z;
for(int i=1;i<=e;i++){
x=read(),y=read(),z=read();
dis[x][y]=min(dis[x][y],z);
dis[y][x]=dis[x][y];
}
floyd();
for(int i=0;i<=v;i++) dis[i][i]=0;
for(int i=0;i<=n;i++){
for(int j=0;j<=m;j++){
dp[i][j][0]=dp[i][j][1]=inf;
}
}
dp[1][0][0]=dp[1][1][1]=0;
for(int i=2;i<=n;i++)
dp[i][0][0]=dis[c[i-1]][c[i]]+dp[i-1][0][0];
for(int i=2;i<=n;i++){
for(int j=1;j<=m;j++){
dp[i][j][0]=min(dp[i][j][0],min(dp[i-1][j][0]+val(i,i-1,0,0),dp[i-1][j][1]+val(i,i-1,0,1)));
dp[i][j][1]=min(dp[i][j][1],min(dp[i-1][j-1][0]+val(i,i-1,1,0),dp[i-1][j-1][1]+val(i,i-1,1,1)));
}
}
double ans=99999999.0;
for(int i=0;i<=m;i++)
ans=min(dp[n][i][0],min(ans,dp[n][i][1]));
printf("%.2lf",ans);
return 0;
}