[Noip2016] 换教室
题目描述
对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程。
在可以选择的课程中,有 2n 节课程安排在 n 个时间段上。在第 i ( 1≤i≤n)个时间段上,两节内容相同的课程同时在不同的地点进行,其中,牛牛预先被安排在教室 ci 上课,而另一节课程在教室 di 进行。
在不提交任何申请的情况下,学生们需要按时间段的顺序依次完成所有的 n 节安排好的课程。如果学生想更换第 i 节课程的教室,则需要提出申请。若申请通过,学生就可以在第 i 个时间段去教室 di 上课,否则仍然在教室 ci 上课。
由于更换教室的需求太多,申请不一定能获得通过。通过计算,牛牛发现申请更换第 i 节课程的教室时,申请被通过的概率是一个已知的实数 ki ,并且对于不同课程的申请,被通过的概率是互相独立的。
学校规定,所有的申请只能在学期开始前一次性提交,并且每个人只能选择至多 m 节课程进行申请。这意味着牛牛必须一次性决定是否申请更换每节课的教室,而不能根据某些课程的申请结果来决定其他课程是否申请;牛牛可以申请自己最希望更换教室的 m 门课程,也可以不用完这 m 个申请的机会,甚至可以一门课程都不申请。
因为不同的课程可能会被安排在不同的教室进行,所以牛牛需要利用课间时间从一间教室赶到另一间教室。
牛牛所在的大学有 v 个教室,有 e 条道路。每条道路连接两间教室,并且是可以双向通行的。由于道路的长度和拥堵程度不同,通过不同的道路耗费的体力可能会有所不同。 当第 i( 1≤i≤n−1 )节课结束后,牛牛就会从这节课的教室出发,选择一条耗费体力最少的路径前往下一节课的教室。
现在牛牛想知道,申请哪几门课程可以使他因在教室间移动耗费的体力值的总和的期望值最小,请你帮他求出这个最小值。
Hint
-
道路中可能会有多条双向道路连接相同的两间教室。 也有可能有道路两端连接的是同一间教室。
-
请注意区分n,m,v,e的意义, n不是教室的数量, m不是道路的数量。
Solution
懒得一个一个加美元符了就把输入输出格式删了啊。。。
看到这题首先想到的就是 \(floyd\) 加上期望 \(dp\) 处理。
那么就容易定义 \(dp[i][j]\) 表示已经决策完了前 \(i\) 个教室,申请了 \(j\) 个的期望体力消耗。但是接下来怎么办呢?注意到转移是和当前与上次的决策都有关的,所以不能单单知道申请了几个,还需要知道当前这个申没申请。
所以 \(dp[i][j][0/1]\) 表示决策完了前 \(i\) 个教室,已经申请了 \(j\) 个,第 \(i\) 个申没申请的期望体力消耗。那么答案就是 \(\min \limits_{0\leq i\leq m} (\min(dp[n][i][0],d[n][i][1]))\) 。
怎么转移呢?再次观察到决策分为两类,一类是当前申请了,一类是没申请。
其中每类决策又分两类,是上一个是否申请了。
于是转移就很好想了。又因为期望是概率乘以数值,所以转移的时候把每个情况的概率乘上要走的路就是 \(dp[i]\) 了。
转移:
\(dp[i][j][0]=\min (dp[i-1][j][0]+mp[c[i-1]][c[i]],dp[i-1][j][1]+k[i-1]*mp[d[i-1]][c[i]]+(1.0-k[i-1])*mp[c[i-1]][c[i]])\)
\(dp[i][j][1]=\min(dp[i-1][j-1][0]+k[i]*mp[c[i-1]][d[i]]+(1-k[i])*mp[c[i-1]][c[i]],dp[i-1][j-1][1]+k[i-1]*k[i]*mp[d[i]][d[i-1]]+(1.0-k[i-1])*k[i]*mp[c[i-1]][d[i]]+k[i-1]*(1.0-k[i])*mp[d[i-1]][c[i]]+(1.0-k[i-1])*(1.0-k[i])*mp[c[i-1]][c[i]])\)
Code
#include<cstdio>
#include<cctype>
#include<cstring>
#define M 305
#define N 2005
#define db double
#define min(A,B) ((A)<(B)?(A):(B))
#define max(A,B) ((A)>(B)?(A):(B))
int n,m,v,e;
int mp[M][M];
int c[N],d[N];
db k[N],dp[N][N][5];
int getint(){
int x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x;
}
signed main(){
n=getint(),m=getint(),v=getint(),e=getint();
for(int i=1;i<=n;i++)
c[i]=getint();
for(int i=1;i<=n;i++)
d[i]=getint();
for(int i=1;i<=n;i++)
scanf("%lf",&k[i]);
memset(mp,0x3f,sizeof mp);
for(int i=1;i<=v;i++)
mp[i][i]=0;
for(int i=1;i<=e;i++){
int a=getint(),b=getint(),c=getint();
mp[a][b]=mp[b][a]=min(mp[a][b],c);
}
for(int i=1;i<=v;i++){
for(int j=1;j<=v;j++){
for(int k=1;k<=v;k++)
mp[j][k]=min(mp[j][k],mp[j][i]+mp[i][k]);
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
for(int k=0;k<=1;k++)
dp[i][j][k]=1e9;
}
}
dp[1][0][0]=dp[1][1][0]=dp[1][1][1]=0.0;
for(int i=2;i<=n;i++){
dp[i][0][0]=dp[i-1][0][0]+(db)mp[c[i-1]][c[i]];
for(int j=1;j<=m;j++){
//calc dp[i][j][..]
dp[i][j][0]=min(dp[i-1][j][0]+mp[c[i-1]][c[i]],dp[i-1][j][1]+k[i-1]*mp[d[i-1]][c[i]]+(1.0-k[i-1])*mp[c[i-1]][c[i]]);
dp[i][j][1]=min(dp[i-1][j-1][0]+k[i]*mp[c[i-1]][d[i]]+(1-k[i])*mp[c[i-1]][c[i]],dp[i-1][j-1][1]+k[i-1]*k[i]*mp[d[i]][d[i-1]]+(1.0-k[i-1])*k[i]*mp[c[i-1]][d[i]]+k[i-1]*(1.0-k[i])*mp[d[i-1]][c[i]]+(1.0-k[i-1])*(1.0-k[i])*mp[c[i-1]][c[i]]);
//printf("i=%d,j=%d,dp0=%.2lf,dp1=%.2lf\n",i,j,dp[i][j][0],dp[i][j][1]);
}
}
db minn=1e9;
for(int i=0;i<=m;i++){
//printf("i=%d,dp0=%.2lf,dp1=%.2lf\n",i,dp[n][i][0],dp[n][i][1]);
minn=min(minn,min(dp[n][i][0],dp[n][i][1]));
}
printf("%.2lf\n",minn);
return 0;
}