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

来源:互联网 发布:淘宝店铺装潢 编辑:程序博客网 时间:2024/06/05 06:18

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

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

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


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

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-12using 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-12using 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-12using 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]]);    }}



0 0
原创粉丝点击