高斯消元入门⑥-配合求解概率/期望dp-HDU4870

2023大厂真题提交网址(含题解):

www.CodeFun2000.com(http://101.43.147.120/)

最近我们一直在将收集到的机试真题制作数据并搬运到自己的OJ上,供大家免费练习,体会真题难度。现在OJ已录入50+道2023年最新大厂真题,同时在不断的更新。同时,可以关注"塔子哥学算法"公众号获得每道题的题解。
在这里插入图片描述

前言:

对于期望dp/概率dp,我们有状态以及状态转移方程.我们将所有的状态看成未知数,状态转移方程看成线性方程。那么就得到了一个线性方程组.

适用情形:

状态转移有后效性时

优化点:

大多数情况下它会是一个稀疏矩阵(因为一个转移方程所牵涉到的状态比较少).所以在 消元的时候,遇到0元素就continue. 这个trick可以将复杂度从三次方优化到近似二次方.(前提是要是一个稀疏矩阵.)

题目一:HDU4780

题目大意:

你有两个数 A , B A,B A,B。 每次你会选择 m i n ( A , B ) min(A,B) min(A,B)。然后有 p p p的概率使得其 + 1 +1 +1.有 1 − p 1-p 1p的概率使得其 − 2 -2 2.(不足 2 2 2,那么将其归 0 0 0).问你期望多少次使得其有一个数达到 20 20 20.

题目思路:
方法一:暴力高斯消元

f ( i , j )    ( i ≥ j ) f(i,j)\ \ (i \geq j) f(i,j)  (ij)代表从 ( i , j ) (i,j) (i,j)到其中一个数达到20所期望的次数.

根据期望的逆推法,我们有:
f ( 20 , x ) = 0   ,   x ∈ [ 0 , 20 ] f(20,x)=0 \ ,\ x \in [0,20] f(20,x)=0 , x[0,20]

f ( i , j ) = 1 + p ∗ f ( i , j − 1 ) + ( 1 − p ) ∗ f ( i , j + 2 ) f(i,j) = 1+p*f(i,j-1)+(1-p)*f(i,j+2) f(i,j)=1+pf(i,j1)+(1p)f(i,j+2)

因为这个转移方程有后效性,所以我们使用高斯消元解方程组.将状态 ( i , j ) (i,j) (i,j)编号.列出含有 i ∗ j i*j ij个未知数, i ∗ j i*j ij个方程的方程组,跑高斯消元即可.又因为显然这些转移方程是线性无关的,所以一定有解,且只有唯一解.

优化的时候还要注意一点,在 f a b s ( . . ) ≤ e p s fabs(..) \leq eps fabs(..)eps的时候,一定要注意 e p s eps eps的精度够不够。因为有时候结果很大是因为除法的分母很小。所以要注意 e p s eps eps的精度要够大.

时间复杂度: O ( u p 6 ) O(up^6) O(up6) 由于是稀疏矩阵,趋近于 O ( u p 4 ) O(up^4) O(up4)

1.long double 3.4s
2.double 0.8s
3.根据稀疏矩阵加优化 0.1s

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
const int maxn = 1e5 + 5;
const int mod = 1e9 + 7;
const double eps = 1e-12;
map<pii , int> mp;
double a[300][300];
int cnt = 0;
int up = 20;
int main()
{
    for (int i = up ; i >= 0 ; i--)
        for (int j = i ; j >= 0 ; j--)
            mp[make_pair(i,j)] = ++cnt;
    double p;
    while (~scanf("%lf" , &p)){
        // 初始化边界条件: f(20 , x) = 0
        for (int i = 0 ; i <= cnt ; i++)
            for (int j = 0 ; j <= cnt + 1; j++)
                a[i][j] = 0.0;
        for (int i = 0 ; i <= up ; i++){
            int x = mp[make_pair(up , i)];
            a[x][x] = 1;
        }
        for (int i = up - 1 ; i >= 0 ; i--)
            for (int j = i ; j >= 0 ; j--){
                // f(i,j) = 1 + f(i , j + 1) * p + f(max(i , j - 2) , min(i , j - 2)) * (1 - p)
                // f(i,j) - p * f(i , j + 1)  - (1 - p) * f(max(i , j - 2) , min(i , j - 2))  = 1
                int x = mp[make_pair(i , j)];
                int y = mp[make_pair(max(i , j + 1) , min(i , j + 1))];
                int z = mp[make_pair(max(i , max(j - 2 , 0)) , min(i , max(j - 2 , 0)))];
                a[x][x] += 1;
                a[x][y] += -p;
                a[x][z] += -(1 - p);
                a[x][cnt + 1] = 1;
            }
        // 构造好系数矩阵后跑高斯消元 稀疏矩阵的复杂度很低
        int r , c;
        for (r = c = 1 ; r <= cnt && c <= cnt ; r++ , c++){
            int p = r;
            for (int j = r + 1 ; j <= cnt ; j++){
                if (fabs(a[j][c]) > fabs(a[r][c])){
                    p = j;
                }
            }
            // 会不会有自由元呢? 不会的
            if (fabs(a[p][c]) < eps) {
                r--;
                continue;
            }
            swap(a[r] , a[p]);
            for (int j = 1 ; j <= cnt ; j++){
                if (j == r) continue;
                if (fabs(a[j][c]) < eps) continue; // 对付稀疏矩阵的trick
                double d = a[j][c] / a[r][c];
                for (int k = c ; k <= cnt + 1 ; k++)
                    a[j][k] -= d * a[r][k];
            }
        }
        int e = mp[make_pair(0,0)];
        double ans = a[e][cnt + 1] / a[e][e];
        printf("%.6f\n" , ans);
    }
    return 0;
}
进一步优化:

由于取 m i n min min的特点,状态的变化一定是 f ( 0 , 0 ) → f ( 0 , 1 ) → f ( 1 , 1 ) → f ( 1 , 2 ) − > → f ( 2 , 2 ) → . . . → f ( 19 , 20 ) f(0,0) \rightarrow f(0,1) \rightarrow f(1,1) \rightarrow f(1,2) -> \rightarrow f(2,2) \rightarrow ... \rightarrow f(19,20) f(0,0)f(0,1)f(1,1)f(1,2)>→f(2,2)...f(19,20)

可以看出花费就是一个到达19分,一个到达20分的花费,等于 2 ∗ f ( 0 ) − f ( 19 ) 2*f(0)-f(19) 2f(0)f(19)(两个到达20的花费减去一个从19到20的期望花费).

状态变化的过程不难看出:两个维度的变化是互不相干的。那么这个问题拓展到 k k k维也一样.

k k k维的情况下,复杂度就是 k ∗ f ( 0 ) − ( k − 1 ) ∗ f ( 19 ) k*f(0)-(k-1)*f(19) kf(0)(k1)f(19)

复杂度与k无关,为 O ( u p 3 ) O(up^3) O(up3)

方法二:递推法绕开高斯消元,解除后效性 , 线性复杂度

这题还能使用递推法,那么分析过程就很像我之前AK那一场的题了:牛客OI周赛14-普及组

而且思想在:wannay-概率dp 视频中敦爷也介绍过了.

思路:

无论我们怎么反复横跳,过程也许很波折,但是大方向一定是从前往后慢慢进行的。所以我们不妨将这个问题分阶段进行。令 t i t_i ti代表第一次到达 i i i分数的期望次数.根据上述优化可知 t 20 + t 19 t_{20}+t_{19} t20+t19所求.

考虑转移:

1.固定部分:想要到 i i i.先得到 i − 1 i-1 i1.花费 = f i − 1 + 1 = f_{i-1}+1 =fi1+1.

解释: f i f_i fi是显然的,而 + 1 +1 +1是因为,我们无论这次到或者不到,都必须试一下。所以 + 1 +1 +1

2.概率产生的花费:

p p p的概率成功,成功了,那无事发生,花费包含在固定部分了(可以考虑 p = 1 p=1 p=1的极端情况.)

1 − p 1-p 1p的概率失败。失败后我们到达状态 i − 3 i-3 i3.从 i − 3 到 i i-3到i i3i的花费可以表示成: d i = f i − f i − 3 d_i=f_{i}-f_{i-3} di=fifi3

所以 f i = f i − 1 + 1 + ( 1 − p ) ∗ ( f i − f i − 3 ) f_i=f_{i - 1}+1+(1-p)*(f_i-f_{i-3}) fi=fi1+1+(1p)(fifi3)

   ⟹    f i = f i − 1 + ( p − 1 ) ∗ f i − 3 + 1 p \implies f_i=\frac{f_{i-1}+(p-1)*f_{i-3}+1}{p} fi=pfi1+(p1)fi3+1

f 0 = 0 f_0=0 f0=0
f 1 = 1 p f_1=\frac{1}{p} f1=p1
f 2 = f 1 + ( p − 1 ) ∗ f 0 + 1 p = 1 p + 1 p 2 f_2=\frac{f_{1}+(p-1)*f_{0}+1}{p}=\frac{1}{p}+\frac{1}{p^2} f2=pf1+(p1)f0+1=p1+p21

时间复杂度: O ( n ) O(n) O(n)

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值