高斯消元法 概率dp 蛇梯棋 Snakes and Ladders LightOJ - 1151 详解

前言:

高斯消元法,用于解方程组

变为矩阵形式

变为对角线上值为1,其余值为0的格式,则后面一列即为对应变量的值(线性代数知识)

具体实现参考这个博客:https://blog.csdn.net/lzyws739307453/article/details/89816311

代码模板:

double a[110][110];
int n=100;
void build()
{
   构建矩阵
}
double ans[110];

void gauss()
{
    for(int i = 1; i <= n; ++i)
    {
        int r = i;
        for(int j = i + 1; j <= n; ++j)
            if(fabs(a[r][i]) < fabs(a[j][i]))
                r = j;
        if(i != r)
            swap(a[i],a[r]);
        double div = a[i][i];
        for(int j = i; j <= n + 1; ++j)
            a[i][j] /= div;
        for(int j = i + 1; j <= n; ++j)
        {
            div = a[j][i];
            for(int k = i; k <= n + 1; ++k)
                a[j][k] -= a[i][k] * div;
        }
    }
    ans[n] = a[n][n+1];
    for(int i = n - 1; i >= 1; --i)
    {
        ans[i] = a[i][n+1];
        for(int j = i + 1; j <= n; ++j)
            ans[i] -= (a[i][j] * ans[j]);
    }
}

下面看这道题:题目链接

题意:有一个棋盘,棋盘上有梯子和蛇,梯子可从下端直接到上端,蛇也是从蛇头到蛇尾。一个六面骰子,投到几点走几步,求从1到100期望的投骰子次数。

先不考虑蛇和梯子,当我们走到末尾的时候投骰子会有越界情况,所以dp是从后往前推的,越界时呆在原地不动但是投骰子次数要+1

设dp[ i ] 为走到第i 个格子期望次数

没有越界情况:dp [ i ]= ( dp[ i+1 ]+dp[ i+2 ]+dp[ i+3 ]+dp[ i+4 ]+dp[ i+5 ]+dp[ i+6 ]+6)/6;             

有越界情况(i=96):dp [ i ]=(dp[ i+1 ] +dp[ i+2 ]+dp[ i+3 ]+dp[ i+4]+dp[ i ]+dp[ i ]+6)/6;(这里是i =96时的公式,具体dp[ i ]个数根据范围判断)

考虑蛇和梯子 都是从a->b;所以公式为 dp[ a ]=dp[ b ];(本题是从出发点思考的,所以这里的式子也要这样写。)

存在环形(a,b大小不确定),需要高斯消元来解决。

构建二维矩阵,a[110][110]

将所有的未知数移到等号左侧,并将系数写入对应的二维矩阵中,将等号右边的值赋给a[ ] [n+1]

代码

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
#define ll long long
using namespace std;
#define inf 0x3f3f3f3f
int n=100;
int book[110];
double a[110][110];
void build()
{
    for(int i=1; i<n; i++)
    {
        if(book[i])     //如果该行已有公式
            continue;
        for(int k=1; k<=6; k++)
        {
            if(i+k<=100)
                a[i][i+k]=(-1.0/6.0);
            else
                a[i][i]-=(1.0/6.0);  //越界
        }
        a[i][i]+=1;
        a[i][n+1]=1;  //等式右边的值
    }
    a[n][n]=1,a[n][n+1]=0;
}
double ans[110];

void gauss()
{
    for(int i = 1; i <= n; ++i)
    {
        int r = i;
        for(int j = i + 1; j <= n; ++j)
            if(fabs(a[r][i]) < fabs(a[j][i]))
                r = j;
        if(i != r)
            swap(a[i],a[r]);
        double div = a[i][i];
        for(int j = i; j <= n + 1; ++j)
            a[i][j] /= div;
        for(int j = i + 1; j <= n; ++j)
        {
            div = a[j][i];
            for(int k = i; k <= n + 1; ++k)
                a[j][k] -= a[i][k] * div;
        }
    }
    ans[n] = a[n][n+1];
    for(int i = n - 1; i >= 1; --i)
    {
        ans[i] = a[i][n+1];
        for(int j = i + 1; j <= n; ++j)
            ans[i] -= (a[i][j] * ans[j]);
    }
}
int main()
{
    int t,q;
    scanf("%d",&t);
    int op=0;
    while(t--)
    {
        memset(book,0,sizeof(book));
        memset(a,0,sizeof(a));
        memset(ans,0,sizeof(ans));
        scanf("%d",&q);
        for(int i=0; i<q; i++)
        {
            int x,y;
            scanf("%d %d",&x,&y);
            book[x]=1;
            a[x][x]=1;
            a[x][y]=-1;
        }
        build();
        gauss();
        printf("Case %d: %lf\n",++op,ans[1]);

    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值