LOJ-1151 Snakes and Ladders

这篇博客介绍了如何利用动态规划解决一个名为 snakes and ladders 的棋盘游戏结束时的期望掷骰子次数问题。通过建立状态转移方程,博主详细分析了不同情况下的转移条件,并利用高斯消元法求解方程组,得出最终答案。文章深入浅出地阐述了动态规划在解决这类问题中的应用。
摘要由CSDN通过智能技术生成

Snakes and Ladders

题目链接

题目大意: 100 100 100 个格子,我们起始点在 1 1 1 ,每次抛一个六面的骰子,掷到几就往前走几步,这中间会有梯子或者蛇,实际上都是传送门,一个是往前传,一个是往后传,走到 100 100 100 则游戏结束,如果将要走的地方是出界的,那么将重新掷骰子,现在给出 n n n 个传送门的信息 u − > v u->v u>v ,计算游戏结束时掷骰子的次数期望,题目保证肯定有机会结束游戏。

我们设从 i i i 出发结束游戏的掷骰子的次数期望为 d p [ i ] dp[i] dp[i] ,则我们可以列出下面三种情况的转移方程。

1.若有传送门: d p [ i ] = d p [ t o [ i ] ] dp[i] = dp[to[i]] dp[i]=dp[to[i]]
说明:传送门是不需要掷骰子的,所以此位置有传送门肯定就等于传送点的期望。

2.若其不会越界: d p [ i ] = ∑ j = i + 1 i + 6 ( 1 6 ∗ ( d p [ j ] + 1 ) ) = ∑ j = i + 1 i + 6 ( 1 6 ∗ d p [ j ] ) + 1 dp[i] = \sum\limits_{j=i+1}^{i+6}(\frac{1}{6}*(dp[j]+1))=\sum\limits_{j=i+1}^{i+6}(\frac{1}{6}*dp[j])+1 dp[i]=j=i+1i+6(61(dp[j]+1))=j=i+1i+6(61dp[j])+1
说明:那不会越界的位置的期望就分别由六种情况组成,且这六种情况是等概率的,所以可以列出上式。

3.若其可能越界: d p [ i ] = ∑ j = i + 1 100 ( 1 6 ∗ ( d p [ j ] + 1 ) ) + ( i + 6 − 100 6 ∗ ( d p [ i ] + 1 ) ) dp[i]=\sum\limits_{j=i+1}^{100}(\frac{1}{6}*(dp[j]+1))+(\frac{i+6-100}{6}*(dp[i]+1)) dp[i]=j=i+1100(61(dp[j]+1))+(6i+6100(dp[i]+1))
也就是 d p [ i ] = ( i + 6 − 100 6 ∗ d p [ i ] ) + ∑ j = i + 1 100 ( 1 6 ∗ d p [ j ] ) + 1 dp[i]=(\frac{i+6-100}{6}*dp[i])+\sum\limits_{j=i+1}^{100}(\frac{1}{6}*dp[j])+1 dp[i]=(6i+6100dp[i])+j=i+1100(61dp[j])+1
说明:分两种情况去考虑,第一种就是界内,另一种就是界外,界内的和上一种情况同理计算。那么界外的期望部分实际上就是: 抛 到 界 外 的 概 率 × ( 1 + 后 续 掷 骰 子 期 望 次 数 ) 抛到界外的概率 × (1+后续掷骰子期望次数) ×(1+) ,后续掷骰子的期望次数不就是相当于重来一次嘛,那么自然就是 d p [ i ] dp[i] dp[i]

我们发现这里因为存在下行传送门的缘故,无法线性的直接递推过去,但是我们发现我们可以列出 100 100 100 个方程,如下

对于每一个位置 i ( i ≠ 100 ) i(i≠100) i(i=100) ,我们都可以列出一个方程

1.若有传送门: x i − x t o = 0 x_i-x_{to}=0 xixto=0

2.若其不会越界( i ≥ 94 i≥94 i94 ): x i + ∑ j = i + 1 i + 6 ( − 1 6 ∗ x j ) + 1 = 0 x_i+\sum\limits_{j=i+1}^{i+6}(-\frac{1}{6}*x_{j})+1=0 xi+j=i+1i+6(61xj)+1=0

3.若其会越界: 100 − i 6 ∗ x i + ∑ j = i + 1 100 ( − 1 6 ∗ x j ) + 1 = 0 \frac{100-i}{6}*x_i+\sum\limits_{j=i+1}^{100}(-\frac{1}{6}*x_j)+1=0 6100ixi+j=i+1100(61xj)+1=0

实际上呢, 2 , 3 2,3 2,3 两点是可以合并的: m i n ( 6 , 100 − i ) 6 ∗ x i + ∑ j = i + 1 m i n ( i + 6 , 100 ) ( − 1 6 ∗ x j ) + 1 = 0 \frac{min(6,100-i)}{6}*x_i+\sum\limits_{j=i+1}^{min(i + 6,100)}(-\frac{1}{6}*x_j)+1=0 6min(6,100i)xi+j=i+1min(i+6,100)(61xj)+1=0

于是最终我们就可以得到两种情况:

1.有传送门: x i − x t o = 0 x_i-x_{to}=0 xixto=0

2.无传送门: m i n ( 6 , 100 − i ) 6 ∗ x i + ∑ j = i + 1 m i n ( i + 6 , 100 ) ( − 1 6 ∗ x j ) + 1 = 0 \frac{min(6,100-i)}{6}*x_i+\sum\limits_{j=i+1}^{min(i + 6,100)}(-\frac{1}{6}*x_j)+1=0 6min(6,100i)xi+j=i+1min(i+6,100)(61xj)+1=0

于是我们就可以列出 100 100 100 (包括已知的 x 100 = 0 x_{100}=0 x100=0 )个方程,按照题中说的,我们是一定可以走到终点的,也就是说,这个方程是一定有解的。

最终我们用高斯消元求解,输出 x 1 x_1 x1 即可

#include <bits/stdc++.h>

using namespace std;

const int N = 1e2 + 10;

const double eps = 1e-10;

int n, to[N], cs;

double g[N][N];

double x[N];

void build() {
    for (int i = 1; i < 100; ++i) {
        if (~to[i]) {
            g[i][i] = 1;
            g[i][to[i]] = -1;
        }
        else {
            g[i][i] = 1.0 * (min(6, 100 - i)) / 6;
            for (int j = i + 1; j <= min(i + 6, 100); ++j) {
                g[i][j] = -1.0 / 6;
            }
            g[i][101] = 1;
        }
    }
    g[100][100] = 1;
}

void gj(int n) {
    for(int i = 1; i <= n; i++) {
        int r = i;
        for(int j = i + 1; j <= n; j++) {
            if(fabs(g[r][i]) < fabs(g[j][i])) {
                r = j;
            }
        }
        if(r != i) {
            for(int j = 1; j <= n + 1; j++) {
                swap(g[i][j], g[r][j]);
            }
        }
        for(int j = 1; j <= n; j++) {
            if(j != i) {
                double t = g[j][i] / g[i][i];
                for(int k = i + 1; k <= n + 1; k++) {
                    g[j][k] -= g[i][k] * t;
                }
            }
        }
    }
    for(int i = 1; i <= n; i++) {
        x[i] = g[i][n + 1] /= g[i][i];
    }
} 

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    int T;
    scanf("%d", &T);
    while(T--) {
        memset(to, -1, sizeof to);
        memset(g, 0, sizeof g);
        scanf("%d", &n);
        for (int i = 0, u, v; i < n; ++i) {
            scanf("%d%d", &u, &v);
            to[u] = v;
        }
        build();
        gj(100);
        printf("Case %d: %.10lf\n", ++cs, x[1]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值