[概率DP] LightOJ 1151 - Snakes and Ladders

1151 - Snakes and Ladders
题意:
有100个格子,从1开始走,每次随机走1~6。有n个格子会单向传送到其他格子,tp[i]表示从i传送到tp[i]。
1和100不会有传送,一个格子也不会有两种传送。问走到100的期望值,注意不能走到100以外。
题解:
容易看出是个期望值DP,用 DP[i] 表示从第 i 个格子走出去需要走的次数,有两种情况。
1. i 个格子有传送: DP[i]=DP[tp[i]]
2. i 个格子没传送,需要考虑会不会走到100以外。
可以表示为 DP[i]=16kj=1DP[i+j]+166j=k+1DP[i]+1,其中 i+k=100k<=6
整理一下就得到了两个方程。
DP[i]=kj=1DP[i+j]+6k
DP[i]=DP[tp[i]]
因为 tp[i] 可以大于 i ,也可以小于i,第二个方程需要高斯消元,再整理成矩阵方程形式。
kDP[i]kj=1DP[i+j]=6
DP[i]DP[tp[i]]=0
这样建立矩阵,套上高斯消元即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 105;
const double esp = 1e-8;
int tp[N];
double mat[N][N];
double x[N];
int free_x[N];
int Gauss(int equ, int var){
    int k;
    int max_r, col;
    int free_index, free_num;
    memset(free_x, 1, sizeof(free_x));
    memset(x, 0, sizeof(x));
    for(k = col = 0; k < equ && col < var; ++k, ++col){
        max_r = k;
        for(int i = k+1; i < equ; ++i){
            if(fabs(mat[i][col]) - mat[max_r][col] > esp) max_r = i;
        }
        if(max_r != k ){
            for(int j = k; j < var+1; ++j) swap(mat[max_r][j], mat[k][j]);
        }
        if(fabs(mat[k][col] < esp)){ --k; continue; }
        for(int i = k+1; i < equ; ++i){
            if(fabs(mat[i][col]) <= esp) continue;
            double tmp = mat[i][col] / mat[k][col];
            for(int j = col; j < var+1; ++j){
                mat[i][j] -= mat[k][j]*tmp;
            }
        }

    }
    for(int i = k; i < equ; ++i){
        if(fabs(mat[i][var] > esp)) return 0;
    }
    if(k < var){
        for(int i = k-1; i >= 0; --i){
            free_num = 0;
            for(int j = 0; j < var; ++j){
                if(fabs(mat[i][j]) > esp && free_x[j]){
                    free_num += 1;
                    free_index = j;
                }
            }
            if(free_num > 1) continue;
            double tmp = mat[i][var];
            for(int j = 0; j < var; ++j){
                if(j != free_index && fabs(mat[i][j]) > esp){
                    tmp -= mat[i][j]*x[j];
                }
            }
            free_x[free_index] = 0;
            x[free_index] = tmp/mat[i][free_index];
        }
        return var-k;
    }
    for(int i = var-1; i >= 0; --i){
        double tmp = mat[i][var];
        for(int j = i+1; j < var; ++j){
            if(fabs(mat[i][j]) > esp){
                tmp -= x[j]*mat[i][j];
            }
        }
        x[i] = tmp / mat[i][i];
    }
    return 1;
}
int main(){
    int T, ca = 1;
    scanf("%d", &T);
    while(T--){
        int a, b, n;
        scanf("%d", &n);
        memset(tp, 0, sizeof(tp));
        for(int i = 0; i < n; ++i) {
            scanf("%d%d", &a, &b);
            tp[a] = b;
        }
        memset(mat, 0, sizeof(mat));
        mat[100][100] = 1;
        mat[100][101] = 0;
        for(int i = 1; i < 100; ++i){
            if(tp[i]){
                mat[i][i] = 1;
                mat[i][tp[i]] = -1;
                mat[i][101] = 0;
            }
            else{
                int k = 0;
                for(int j = 1; j <= 6; ++j){
                    if(j+i <= 100){
                        k += 1;
                        mat[i][i+j] = -1;
                    }
                }
                mat[i][i] = k;
                mat[i][101] = 6;
            }
        }
        Gauss(105, 101);
        printf("Case %d: %.8f\n", ca++, x[1]);
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值