P1850 换教室

题面

太长了,不想粘,就甩个链接吧

Link

题解

这个题就是转移方程的分类讨论麻烦了些,其他的没什么。

首先,我们要利用 Floyd 求出每两个点之间的距离 (n的范围那么小,肯定是Floyed 啦

我们就要考虑怎么 dp

我们设 (f[i][j][0/1]) 表示 处理完前 (i) 门课,用了 (j) 次申请,且最后一门课换或者没换的概率。

然后我们就要开始大力讨论。

1. 这次上课没换的期望值

1.上次也没换,期望值就是 f[i-1][j][0] + dis[a[i-1]][[a[i]]

2.上次换了但没通过,他的概率是 1-p[i-1] 期望值就是 dis[a[i-1]][a[i]] * (1-p[i-1])

3.上次换了且通过了, 他的概率是 p[i-1] 期望值就是 p[i-1] * dis[b[i-1]][a[i]]

第二种和第三种情况和在一起就是 f[i-1][j][1] + p[i-1] * dis[b[i-1]][a[i]] + (1-p[i-1]) * dis[a[i-1]][a[i]]

这样,我们就得到了这次不换的转移方程

f[i][j][0] = min(f[i-1][j][0] + 1.0 * dis[a[i-1]][a[i]], f[i-1][j][1] + p[i-1] * dis[b[i-1]][a[i]] + (1-p[i-1]) * dis[a[i-1]][a[i]]);

2.这次要换的期望

上次没换,这次换了没通过的情况 他的概率是 (1-p[i]) 期望就是 (1-p[i]) * dis[a[i-1]][a[i]]

上次没换,这次换了通过的情况,他的概率是 p[i],期望值是 p[i] * dis[a[i-1]][b[i]]

上次换了没通过,这次换了也没通过的情况,概率为 (1-p[i-1]) * (1-p[i]) 期望值就是 (1-p[i-1]) * (1-p[i]) * dis[a[i-1]][a[i]]

上次换了没通过,这次换了通过的情况,概率为 (1-p[i-1]) * p[i] 期望值为 (1-p[i-1]) * p[i] * dis[a[i-1]][b[i]]

上次换了通过了,这次换了没通过的情况,概率为 p[i-1] * (1-p[i]) 期望值为 p[i-1] * (1-p[i]) * dis[b[i-1]][a[i]]

上次换了通过了,这次换了也通过的情况,概率为 p[i-1] * p[i] 期望值为 p[i] * p[i-1] * dis[b[i-1]][b[i]]

然后,前两种情况可以合并在一起由 f[i-1][j-1][0] 转移过来,后四种情况可以合并在一起,由 f[i-1][j-1][1] 转移过来。

具体方程长这样

f[i][j][1] = min(f[i-1][j-1][0] + p[i] * dis[a[i-1]][b[i]] + (1-p[i]) * dis[a[i-1]][a[i]],
                 f[i-1][j-1][1] + p[i] * p[i-1] * dis[b[i-1]][b[i]] + (1-p[i-1]) * (1-p[i]) * dis[a[i-1]][a[i]] + p[i-1] * (1-p[i]) * dis[b[i-1]][a[i]] + (1-p[i-1]) * p[i] * dis[a[i-1]][b[i]]);

初始化

f[1][0][0] = f[1][1][1] = 0.0, 其他都为极大值。

因为 1 是起点,从那边开始都是一样的

转移时注意 (j) 的边界 要特判一下 j == 0 的情况,我就因为这个交了好几遍

Code:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,v,e,x,y,z,w,a[2010],b[2010],dis[310][310];
double p[2010],f[2010][2010][2],ans = 2333333.0;
inline int read()
{
    int s = 0, w = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();}
    return s * w;
}
int main()
{
    n = read(); m = read(); v = read(); e = read();
    for(int i = 1; i <= n; i++) a[i] = read();
    for(int i = 1; i <= n; i++) b[i] = read();
    for(int i = 1; i <= n; i++) scanf("%lf",&p[i]);
    for(int i = 1; i <= v; i++)
    {
        for(int j = 1; j <= v; j++)
        {
            if(i == j) dis[i][j] = 0;
            else dis[i][j] = 23333333;
        } 
    }
    for(int i = 1; i <= e; i++) 
    {
        x = read(); y = read(); w = read();
        dis[x][y] = min(dis[x][y],w);//解决重边问题
        dis[y][x] = min(dis[y][x],w);
    }
    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]);
            }
        }
    }
    for(int i = 1; i <= n; i++) 
    {
    	for(int j = 0; j <= m; j++)
    	{
    		f[i][j][0] = f[i][j][1] = 2333333.0;
    	}
    }
    f[1][0][0] = f[1][1][1] = 0.0;
    for(int i = 2; i <= n; i++)
    {
        for(int j = 0; j <= m; j++)
        {
            f[i][j][0] = min(f[i-1][j][0] + 1.0 * dis[a[i-1]][a[i]], f[i-1][j][1] + p[i-1] * dis[b[i-1]][a[i]] + (1-p[i-1]) * dis[a[i-1]][a[i]]);
            if(j != 0) f[i][j][1] = min(f[i-1][j-1][0] + p[i] * dis[a[i-1]][b[i]] + (1-p[i]) * dis[a[i-1]][a[i]],f[i-1][j-1][1] + p[i] * p[i-1] * dis[b[i-1]][b[i]] + (1-p[i-1]) * (1-p[i]) * dis[a[i-1]][a[i]] + p[i-1] * (1-p[i]) * dis[b[i-1]][a[i]] + (1-p[i-1]) * p[i] * dis[a[i-1]][b[i]]);
        } 
    }
    for(int i = 0; i <= m; i++) ans = min(ans,min(f[n][i][1],f[n][i][0]));//他申请次数可以不用完
    printf("%.2lf",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/genshy/p/13605510.html