关于高斯消元求期望的个人理解

看了一天的高斯消元求期望。

但是貌似因为自己的概率论学的很差,所以总结了一套自己独特的推断。

没有证明,纯粹是个人想法。


个人感觉,做高斯期望求概率的题目分为以下几步:

1.  遍历所有出现的状态,找到所有能达到的状态并重新将其进行编号,可以存在一个num[]。

2. 判断出口(终点)是否被编号,意思就是判断能不能出去(到达),不能到达直接输出不能到达。

3.  遍历所有出现的状态,找到所有标过号的状态 E[num[i]],那么每个E[num[i]]都满足:

     E[num[i]]=(E[num[0]]+step[0])*p[0]+…(E[num[j]]+step[j])*p[j]    其中E[num[0~j]] 为所有E[num[i]] 能达到的状态,step[0~j]为所需要的步数(比如在迷宫图中,step[0~j]=1代表走一步能到),p[0~j] 代表到达这种状态的概率(这个概率不能为零,要在之前给排除,不然会让方程无解!!)

    假设可达状态有cont个,则所有的状态就构成了cont*(cont+1)的增广矩阵。

    这时候再令所有的出口(终点)的期望为0(因为如果当前就在终点就不用走路了,所以期望是0)

4. 运用高斯消元法求解实数增广矩阵(注意精度每次用绝对值最大的行去消元),求解出起点编号所对应的解 x[num[start]] 就是我们要的答案了。


注意点:

1.不能达到的状态一定要舍去,不然会让方程无解。

2.数据记得初始化!

3.为了减少精度丢少,每次消元的时候用绝对值最大的进行消元。

4.注意控制状态数,高斯消元是O(n^3)的算法,若状态过多,则尝试转换需找状态的思路。(例如:要在E(i,j)中i==j的状态,其实可以当做是找状态E(i-j=0)=E(0)的状态)


几道题目:

hdu 2262 Where is the canteen 

题意:n*m的迷宫图,”.“是空地、”@“是起点(仅有一个)、”X“是墙、”$“是终点(不止一个),问从起点到终点的步数期望。

其实很简单就是bfs跑一遍图,然后把点重新编号,不能到的点去掉。

然后在外面遍历一遍n*m建立增广矩阵(其实在bfs里面直接建也可以,但是个人不喜欢这样),然后判断能否走到终点,能就进行高斯消元。

然后求出期望。这题同前面一个题解的公式就不列了。

#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"queue"
#include"algorithm"
#include"iostream"
#define eps 1e-12
using namespace std;
struct node
{
    int x,y;
};
int f;
int n,m;
int equ,var;
char map[20][20];
double a[300][300];
double x[300];
int num[20][20];
int move[4][2]= {{1,0},{0,1},{-1,0},{0,-1}};
void debug()
{
    for(int i=0;i<equ;i++)
    {
        for(int j=0;j<=var;j++) printf("%3.2f ",a[i][j]);
        puts("");
    }
    puts("");
}
int ok(int x,int y)
{
    if(x<0||y<0||x>=n||y>=m) return 0;
    if(map[x][y]=='#') return 0;
    return 1;
}
int bfs(int xx,int yy)
{
    int i,id=0;
    node cur,next;
    queue<node>q;
    cur.x=xx;
    cur.y=yy;
    q.push(cur);
    while(!q.empty())
    {
        cur=q.front();
        q.pop();
        if(num[cur.x][cur.y]!=-1) continue;
        if(map[cur.x][cur.y]=='$') f=1;
        num[cur.x][cur.y]=id++;
        for(i=0; i<4; i++)
        {
            next.x=cur.x+move[i][0];
            next.y=cur.y+move[i][1];
            if(ok(next.x,next.y)) q.push(next);
        }
    }
    return id;
}
void gauss()
{
    int i,j;
    int row,col;
    for(row=0,col=0;row<equ&&col<var;row++,col++)
    {
        int maxr=row;
        for(i=row+1;i<equ;i++) if(fabs(a[i][col])>fabs(a[maxr][col])) maxr=i; //为找绝对值最大的行
        for(i=0;i<=var;i++) swap(a[row][i],a[maxr][i]);
        for(i=row+1;i<equ;i++)
        {
            if(fabs(a[i][col])>eps)
            {
                double ta=a[i][col]/a[row][col];
                for(j=col;j<=var;j++) a[i][j]-=ta*a[row][j];
             }
        }
    }
    //debug();
    for(i=row-1;i>=0;i--)
    {
        double tep=a[i][var];
        for(j=i+1;j<var;j++)
        {
            if(fabs(a[i][j])<=eps) continue;
            tep-=a[i][j]*x[j];
        }
        x[i]=tep/a[i][i];
    }
}

int main()
{
    while(cin>>n>>m)
    {
        int i,j,k;
        for(i=0; i<n; i++) cin>>map[i];
        for(i=0; i<n; i++)
        {
            for(j=0; j<m; j++)
                if(map[i][j]=='@') break;
            if(j!=m) break;
        }
        memset(num,-1,sizeof(num));
        memset(a,0,sizeof(a));
        f=0; //用f判断能不能到达终点
        var=equ=bfs(i,j);
        if(!f)
        {
            puts("-1");
            continue;
        }
        for(i=0;i<n;i++)
        {
            for(j=0;j<m;j++)
            {
                if(num[i][j]==-1) continue;
                int tep=num[i][j];
                int cnt=0;
                for(k=0;k<4;k++)
                {
                    int xx=i+move[k][0];
                    int yy=j+move[k][1];
                    if(xx<0||yy<0||xx>=n||yy>=m) continue;
                    if(map[xx][yy]=='#'||num[xx][yy]==-1) continue;
                    cnt++;
                    a[tep][num[xx][yy]]-=1;
                }
                a[tep][tep]=cnt;
                a[tep][var]=cnt;
                if(map[i][j]=='$')
                {
                    memset(a[num[i][j]],0,sizeof(a[num[i][j]]));
                    a[tep][tep]=1;
                }
            }
        }
        //debug();
        gauss();
        printf("%.6f\n",x[0]);
    }
}

zjut  1317 掷飞盘

http://cpp.zjut.edu.cn/ShowProblem.aspx?ShowID=1317

这题我们以两个飞盘的距离为状态进行转移。
那么E[n]=E[n+2]/4+E[n-2]/4+E[n]/2+1,化简成:2E[n]-E[n+2]-E[n-2]=4。
首先对于两个飞盘给定的起始距离n,我们可以先搜索一下可否到达状态0,如果不行,则直接输出INF。在搜索的过程中,顺便把重新标号也进行了。为什么这题也要重新标号呢?
因为该题中,假设m是偶数,那么对于任意的n,n+1和n-1都是不可达的状态。请一定记得,如果方程组中有不可达的点的话,就会导致无解的情况!

#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"queue"
#include"algorithm"
#include"iostream"
#define exp 1e-12
using namespace std;
int equ,var;
int num[505];
double x[500];
double a[500][500];
int move[4][2]={{1,1},{1,-1},{-1,1},{-1,-1}};
int m,n;
void debug()
{
    for(int i=0; i<equ; i++)
    {
        for(int j=0; j<=var; j++) printf("%3.2f ",a[i][j]);
        puts("");
    }
    puts("");
}

struct node
{
    int x,y;
};
int sign(int x)
{
    return x<0?-x:x;
}
void gauss()
{
    int i,j;
    int row,col;
    for(row=0,col=0; row<equ&&col<var; row++,col++)
    {
        int maxr=row;
        for(i=row+1; i<equ; i++) if(fabs(a[i][col])>fabs(a[maxr][col])) maxr=i;
        for(i=0; i<=var; i++) swap(a[row][i],a[maxr][i]);
        for(i=row+1; i<equ; i++)
        {
            if(fabs(a[i][col])>exp)
            {
                double ta=a[i][col]/a[row][col];
                for(j=col; j<=var; j++) a[i][j]-=a[row][j]*ta;
            }
        }
    }
    for(i=row-1; i>=0; i--)
    {
        double tep=a[i][var];
        for(j=i+1; j<var; j++)
        {
            if(fabs(a[i][j])<=exp) continue;
            tep-=a[i][j]*x[j];
        }
        x[i]=tep/a[i][i];
    }
}

int bfs(int x,int y)   //重标号  标记所有可到达的点
{
    int id=0;
    node cur,next;
    queue<node>q;
    cur.x=x;
    cur.y=y;
    q.push(cur);
    while(!q.empty())
    {
        cur=q.front();
        q.pop();
        if(num[min(sign(cur.x-cur.y),sign(min(cur.x,cur.y)+m-max(cur.x,cur.y)))]!=-1) continue;
        num[min(sign(cur.x-cur.y),sign(min(cur.x,cur.y)+m-max(cur.x,cur.y)))]=id++;
        for(int i=0;i<4;i++)
        {
            next.x=cur.x+move[i][0];
            next.y=cur.y+move[i][1];
            if(next.x==0) next.x=m;
            if(next.y==0) next.y=m;
            if(next.x==m+1) next.x=1;
            if(next.y==m+1) next.y=1;
            q.push(next);
        }
    }
    return id;
}

int main()
{

    while(cin>>m>>n)
    {
        if(n>m/2)             // 注意一下 很关键!
            n=m-n;
        memset(num,-1,sizeof(num));
        memset(a,0,sizeof(a));
        equ=var=bfs(1,1+n);
        if(num[0]==-1)       //到达不了直接输出INF
        {
            puts("INF");
            continue;
        }
        int i;
        for(i=1;i<=m;i++)    //注意一下 i-2和i+2之后的距离变化就好了
        {
            if(num[i]==-1) continue;
            int tep=num[i];
            a[tep][num[sign(i-2)]]-=0.25;
            a[tep][num[min(i+2,m-i-2)]]-=0.25;
            a[tep][tep]-=0.5;
            a[tep][tep]+=1;
            a[tep][var]=1;
        }
        a[num[0]][num[0]]=1;
        gauss();
        printf("%.2f\n",x[num[n]]);
    }
}

hdu 4870 Rating

题意:一个人注册两个账号,初始rating都是0,他每次拿低分的那个号去打比赛,赢了加50分,输了扣100分,胜率为p,他会打到直到一个号有1000分为止,问比赛场次的期望

思路:f(i, j)表示i >= j,第一个号i分,第二个号j分时候,达到目标的期望,那么可以列出转移为f(i, j) = p f(i', j') + (1 - p)f(i'' + j'') + 1
f(i', j')对应的是赢了加分的状态,f(i'', j'')对应输的扣分的状态,跑一遍找出可达状态,利用高斯消元去求解方程组,解出f(0, 0)就是答案。

很明显此题的终点只有一个 f(950,1000)

#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"queue"
#include"algorithm"
#include"iostream"
#define exp 1e-12
using namespace std;
int equ,var;
double x[320];
double a[300][300];
int num[1200][1200];
struct node
{
    int x,y;
};
void debug()
{
    for(int i=0; i<equ; i++)
    {
        for(int j=0; j<=var; j++) printf("%3.2f ",a[i][j]);
        puts("");
    }
    puts("");
}

void gauss()
{
    int i,j;
    int row,col;
    for(row=0,col=0; row<equ&&col<var; row++,col++)
    {
        int maxr=row;
        for(i=row+1; i<equ; i++) if( fabs(a[i][col])>fabs(a[maxr][col])) maxr=i;
        for(i=0; i<=var; i++) swap(a[row][i],a[maxr][i]);
        for(i=row+1; i<equ; i++)
        {
            if(fabs(a[i][col])>exp)
            {
                double ta=a[i][col]/a[row][col];
                for(j=col; j<=var; j++) a[i][j]-=a[row][j]*ta;
            }
        }
    }
    for(i=row-1; i>=0; i--)
    {
        double tep=a[i][var];
        for(j=i+1; j<var; j++)
        {
            if(fabs(a[i][j])<=exp) continue;
            tep-=a[i][j]*x[j];
        }
        x[i]=tep/a[i][i];
    }
}

int bfs(int x,int y)
{
    queue<node>q;
    node cur,next;
    cur.x=x;
    cur.y=y;
    q.push(cur);
    int id=0;
    while(!q.empty())
    {
        cur=q.front();
        q.pop();
        if(num[cur.x][cur.y]!=-1) continue;
        num[cur.x][cur.y]=id++;
        next.x=cur.x+50;
        next.y=cur.y;
        if(next.x<=950)
        {
            if(next.x>next.y) swap(next.x,next.y);
            q.push(next);
        }
        next.x=max(0,cur.x-100);
        next.y=cur.y;
        if(next.x<=950)
        {
            if(next.x>next.y) swap(next.x,next.y);
            q.push(next);
        }
    }
    return id;
}
int main()
{
    double p;
    memset(num,-1,sizeof(num));
    int cont=bfs(0,0);
    num[950][1000]=cont++;
    equ=var=cont;
    while(cin>>p)
    {
        memset(a,0,sizeof(a));
        double q=1-p;
        int i,j,k;
        for(i=0; i<=950; i+=50)
        {
            for(j=i; j<=950; j+=50)
            {
                if(num[i][j]==-1) continue;
                int tep=num[i][j];
                a[tep][tep]+=1;
                a[tep][var]=1;
                int xx,yy;
                xx=max(0,i-100);
                yy=j;
                if(num[xx][yy]!=-1) a[tep][num[xx][yy]]-=q;
                xx=i+50;
                yy=j;
                if(xx>yy) swap(xx,yy);
                if(num[xx][yy]!=-1) a[tep][num[xx][yy]]-=p;
            }
        }
        memset(a[num[950][1000]],0,sizeof(a[num[950][1000]]));
        a[num[950][1000]][num[950][1000]]=1;
        gauss();
        printf("%.6f\n",x[num[0][0]]);

    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值