[luogu]P1850 换教室(概率dp,Floyd最短路)

题意

N个独立的时间段,每个时间段有2节课c_i,d_i对应两个教室,一开始被安排c_i的课,但你可以申请换课,换课申请提交后有p_i概率成功换课.有V个教室,也就是说每节课你需要花时间到指定教室,你想要提交一些申请,使你期望走过的距离和最小.需要注意的是,申请次数有上限M.

数据范围:N,M\leq 2000 ,V\leq 300,E\leq90000(E为连接教室的边的数量,为双向边)

题解

首先可以知道,需要Floyd处理出两两之间的最短路

然后就是申请的问题

如果没有概率p_i那么就是很明显的一个dp

状态为dp[n][m][op],n节课时提交了m个申请且第n节课是否提交申请的最小值

如果加入了概率可以看到dp其实也是成立的,不过要考虑n-1时如果申请是否申请成功即可

 

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

const int MAXN = 5e6 + 5;
int N, M, V, E;
int c[2005], d[2005];
double p[2005];
int dis[305][305];
double dp[2005][2005][2];

int main(){
  int i, j, k;
  scanf("%d%d%d%d", &N, &M, &V, &E);
  for(i = 1; i <= N; i++) scanf("%d", c + i);
  for(i = 1; i <= N; i++) scanf("%d", d + i);
  for(i = 1; i <= N; i++) scanf("%lf", p + i);
  memset(dis, 60, sizeof(dis));
  for(i = 1; i <= E; i++){
    int x, y, w;
    scanf("%d%d%d", &x, &y, &w);
    dis[x][y] = dis[y][x] = min(dis[x][y], w);
  }
  for(i = 1; i <= V; i++) dis[i][i] = 0;
  for(k = 1; k <= V; k++)
    for(i = 1; i <= V; i++)
      for(j = i + 1; j <= V; j++)
        if(dis[i][j] > dis[i][k] + dis[k][j])
          dis[i][j] = dis[j][i] = dis[i][k] + dis[k][j];

  int top;
  for(i = 1; i <= N; i++)
    for(j = 0; j <= M; j++) dp[i][j][1] = dp[i][j][0] = 1e9;
  dp[1][0][0] = dp[1][1][1] = 0;
  for(i = 2; i <= N; i++)
    for(j = 0, top = min(i, M); j <= top; j++){
      if(j){
        dp[i][j][1] = min(dp[i - 1][j - 1][0] +
                          p[i] * dis[c[i - 1]][d[i]] +
                          (1 - p[i]) * dis[c[i - 1]][c[i]],

                          dp[i - 1][j - 1][1] +
                          p[i - 1] * p[i] * dis[d[i - 1]][d[i]] +
                          (1 - p[i - 1]) * p[i] * dis[c[i - 1]][d[i]] +
                          p[i - 1] * (1 - p[i]) * dis[d[i - 1]][c[i]] +
                          (1 - p[i - 1]) * (1 - p[i]) * dis[c[i - 1]][c[i]]);

        dp[i][j][0] = min(dp[i - 1][j][0] +
                          dis[c[i - 1]][c[i]],
                          dp[i - 1][j][1] +
                          p[i - 1] * dis[d[i - 1]][c[i]] +
                          (1 - p[i - 1]) * dis[c[i - 1]][c[i]] );
      }else{
        dp[i][j][0] = dp[i - 1][j][0] + dis[c[i - 1]][c[i]];
      }
    }
  double ans = 1e9;
  for(i = 0, top = min(N, M); i <= top; i++) ans = min(ans, min(dp[N][i][1], dp[N][i][0]));
  printf("%.2lf\n", ans);
  return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值