HDU4418 Time travel【概率DP(高斯消元)】

题目描述:

HDU4418题目传送门
给出一个数轴 [ 0 , n ) [0,n) [0,n),有一个起点和一个终点,某个人一次可以
走1,2,3……m步,走k步的概率为 p k p_k pk,初始有一个方向(左或右),
走到头则掉头返回,问到达终点的期望步数为多少。

题目分析:

明显的一个高斯消元题。
对于可以掉头走我们只需要把0,1,2,…(n-1)序列变成 0,1,2…(n-2),(n-1),(n-2)…2,1,令N=2*(n-1),那么从i点走j步后到达的位置就是(i+j)%N
如果最初的方向为向左,那么令X=N-X,就仍然是向右走了。
f [ i ] f[i] f[i]为从 i i i走到终点 Y Y Y的期望步数,那么 f [ i ] = ∑ j = 1 m p [ j ] ∗ f [ ( i + j ) % N ] f[i]=\sum_{j=1}^mp[j]*f[(i+j)\%N] f[i]=j=1mp[j]f[(i+j)%N]
终点 f [ Y ] = f [ N − Y ] = 0 f[Y]=f[N-Y]=0 f[Y]=f[NY]=0

然而仅仅这样会WA到自闭。
因为我太菜了忘了判无解,我太菜了想当然地以为最后只要解出来系数不为0就有解。。。
如果起点是奇数点终点是偶数点,走两步的概率是100%,那么肯定到不了,方程解出来会有自由元。
于是我们需要事先bfs一波,看起点是否能够走到终点。
其实结合意义想一想只要起点能够走到终点,不能到达终点的点就不会出现在起点到终点的路径中,所以除了bfs其他地方都不会导致impossible,最后放心一除,起点系数不会为一(随意揣测仅供参考可能是错的但是交上去确实能过)。

Code:

#include <bits/stdc++.h>
#define maxn 505
#define LL long long
using namespace std;
const double eps = 1e-8, inf = 1e20;
int T,n,m,X,Y,D;
bool vis[maxn];
double p[maxn],a[maxn][maxn];
inline bool no(double x){return fabs(x)<eps;}
bool Gauss(int N){
    for(int i=0;i<N;i++){
        if(no(a[i][i]))
            for(int j=i+1;j<N;j++)
                if(!no(a[j][i])) {swap(a[i],a[j]);break;}
        if(no(a[i][i])) continue;
        for(int j=0;j<N;j++) if(i!=j&&!no(a[j][i])){
            double t = a[j][i]/a[i][i];
            for(int k=i;k<=N;k++) a[j][k]-=a[i][k]*t;
        }
    }
}
queue<int>q;
bool bfs(){
    q.push(X),vis[X]=1;
    while(!q.empty()){
        int i=q.front();q.pop();
        for(int j=1,v;j<=m;j++) if(p[j]>eps&&!vis[v=(i+j)%n]) vis[v]=1,q.push(v);
    }
    for(int i=0;i<n;i++) if(!vis[i]) a[i][i]=1,a[i][n]=inf;
    return vis[Y]||vis[n-Y];
}
int main()
{
    scanf("%d",&T);
    while(T--){
        memset(a,0,sizeof a);
        memset(vis,0,sizeof vis);
        scanf("%d%d%d%d%d",&n,&m,&Y,&X,&D);
        for(int i=1;i<=m;i++) scanf("%lf",&p[i]),p[i]/=100;
        if(X==Y) {puts("0.00");continue;}
        n=2*(n-1);
        if(D==1) X=n-X;
        if(!bfs()) {puts("Impossible !");continue;}
        for(int i=0;i<n;i++) if(vis[i]){
            if(i==Y||i==n-Y) a[i][i]=1,a[i][n]=0;
            else{
                a[i][i]=1;
                for(int j=1;j<=m;j++) a[i][(i+j)%n]+=-p[j],a[i][n]+=p[j]*j;
            }
        }
        Gauss(n);
        printf("%.2f\n",a[X][n]/a[X][X]);
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值