Problem
嗯,NOIP历年真题什么的,到处都有,抓一道就是三四倍经验题,我就不写题目描述了23333
偷个懒一定不会被人发现的
Solution
Thoughts
事实上,这是我第一次做概率与期望DP的题目,不是很懂套路,然后就想了很久很久,没什么太多的头绪。首先我觉得应该是用f[i][j]保存前i个时间段,申请换j次课,期望收获到的疲劳值。但是注意到这样的状态还是很复杂难以转移,因为状态转移后的状态有两种可能性,他有可能停在c[i]教室,也有可能停在d[i]教室。
可以联想到 [uva-1336修缮长城] 的那道题,通常为了避免复杂状态,我们通过加维来解决。所以用f[i][j][0]保存上次未申请的期望疲劳值,f[i][j][1]表示上次申请的期望疲劳值。但是状态转移方程有点让我纠结。
题外话
在dp的时候,就需要乘上事件发生的概率,我一直在纠结的是,发现好像有些情况下只乘一个概率似乎少了点什么,纠结了很久,最后问了问旁边的dalao,果然如此。这是期望DP的套路啊。感觉吃枣药丸。
Solution
那么由上我想到的思路,就可以很快地得出dp方程,如下。为了让方程看起来好看一点,我们用cc表示从c[i-1]教室到c[i]教室的路径,用cd表示c[i-1]教室到d[i]教室的路径,dc,dd以此类推。
其次,注意到这里的对f[i-1]的调用,每次更新时都只有j,j-1对当前更新的j有影响,那么我们可以采用滚动数组的思想,将j倒叙循环,这样得到的方程可以大大降低空间复杂度。
另外,f[i][0][0]是需要特判的,它仅能从f[i-1][0][0]上转移过来,因此,也有
f[j][0]+=cc;
f
[
j
]
[
0
]
+
=
c
c
;
。需要注意的是,这个更新要在更新完前面的
f[1..j][0/1]
f
[
1..
j
]
[
0
/
1
]
之后才能更新。
Code
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
struct data{
int v,w,nxt;
}edge[180010];
const int INF=0x3f3f3f3f;
int n,m,v,e,p,head[310],c[2010],d[2010],dis[310][310];
double ans=INF,k[2010],f[2010][2];
bool inq[310];
queue<int> q;
template <typename Tp> inline void read(Tp &x)
{
x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
inline double fmin(double x,double y){return x<y?x:y;}
inline void insert(int u,int v,int w)
{
edge[++p].v=v;edge[p].w=w;
edge[p].nxt=head[u];head[u]=p;
edge[++p].v=u;edge[p].w=w;
edge[p].nxt=head[v];head[v]=p;
}
void input()
{
read(n),read(m),read(v),read(e);
int u,v,w;
for(int i=1;i<=n;i++)
read(c[i]);
for(int i=1;i<=n;i++)
read(d[i]);
for(int i=1;i<=n;i++)
scanf("%lf",&k[i]);
for(int i=1;i<=e;i++)
{
read(u),read(v),read(w);
if(u==v)
continue;
insert(u,v,w);
}
}
void spfa(int u)
{
int x,y;
memset(inq,0,sizeof(inq));
for(int i=1;i<=v;i++)
dis[u][i]=INF;
dis[u][u]=0;
while(!q.empty())
q.pop();
q.push(u);
inq[u]=true;
while(!q.empty())
{
x=q.front();
q.pop();
inq[x]=false;
for(int i=head[x];i;i=edge[i].nxt)
{
y=edge[i].v;
if(dis[u][y]>dis[u][x]+edge[i].w)
{
dis[u][y]=dis[u][x]+edge[i].w;
if(!inq[y])
q.push(y),inq[y]=true;
}
}
}
}
int main()
{
int cc,cd,dc,dd;
double t;
input();
for(int i=1;i<=v;i++)
spfa(i);
for(int j=0;j<=m;j++)
f[j][0]=f[j][1]=INF;
f[0][0]=f[1][1]=0.0;
for(int i=2;i<=n;i++)
{
cc=dis[c[i-1]][c[i]];cd=dis[c[i-1]][d[i]];
dc=dis[d[i-1]][c[i]];dd=dis[d[i-1]][d[i]];
for(int j=(i<m?i:m);j>=1;j--)
{
t=fmin(f[j][0]+cc,f[j][1]+k[i-1]*dc+(1.0-k[i-1])*cc);//上次不换/换了
f[j][1]=fmin(f[j-1][0]+k[i]*cd+(1.0-k[i])*cc,f[j-1][1]+dd*k[i-1]*k[i]+cd*(1.0-k[i-1])*k[i]+dc*k[i-1]*(1.0-k[i])+cc*(1.0-k[i-1])*(1.0-k[i]));//上次不换/换了
f[j][0]=t;
}
f[0][0]+=cc;
}
for(int j=0;j<=m;j++)
{
if(ans>f[j][0])
ans=f[j][0];
if(ans>f[j][1])
ans=f[j][1];
}
printf("%.2lf\n",ans);
return 0;
}