First Knight UVA - 12177 (期望步数/高斯消元/优化)

传送门

题意:有一个矩阵,算出从(0,0)到(n,m)的期望步数,每一个矩阵单元给出了向下,向右,向上,向左的概率,保证三者相加等于1,除了最后一个到达的位置相加全部等于0.

题解:附上传送门,看了后再说点自己的理解,首先得理解怎么算出期望步数,他是与自己走向别人的概率有关的东西,不是别人走向自己的概率,然后根据再根据期望公式列出来,就知道了是走向其他块的概率乘以其他块的期望步数,然后再加上相应的权值,最后将这个常量放到右边,每一个块都可以写出来,然后进行高斯消元,但是普通的高斯消元会T到死,然后进行优化,只要能化为一个上三角矩阵,这样普通写,大概能有2s。

附上代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

const int inf=0x3f3f3f3f;
const int maxn=51;
const int maxm=100805;

int n,m;
double p[maxn][maxn][4];
double a[maxn*maxn][maxn*maxn],b[maxn*maxn];
int dir[][2]={{1,0},{0,1},{-1,0},{0,-1}};
int r,c;

double gaosi()
{
    for(int i=r-1;i>=0;i--){
        for(int j=i-1;j>=0;j--){
            double temp=a[j][i]/a[i][i];
            if(temp==0){
                continue;
            }
            for(int k=i;k>=0;k--){
                a[j][k]-=a[i][k]*temp;
            }
            b[j]-=b[i]*temp;
        }
    }
    return b[0]/a[0][0];
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF&&n&&m){
        for(int k=0;k<4;k++){
            for(int i=0;i<n;i++){
                for(int j=0;j<m;j++){
                    scanf("%lf",&p[i][j][k]);
                }
            }
        }
        r=n*m;
        memset(a,0,sizeof(a));
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                int id=i*m+j;
                b[id]=a[id][id]=1.0;
                if(i==n-1&&j==m-1){
                    a[id][id]=1.0;
                    continue;
                }
                for(int k=0;k<4;k++){
                    int x=i+dir[k][0];
                    int y=j+dir[k][1];
                    if(x<0||x>=n||y<0||y>=m){
                        continue;
                    }
                    a[id][x*m+y]=-p[i][j][k];
                }
            }
        }
        b[r-1]=0;
        printf("%.7lf\n",gaosi());
    }
    return 0;
}

第二种优化,就是一行一行消,论文中有相应的介绍,因为其他地方都是0,跑完大概860ms

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

const int inf=0x3f3f3f3f;
const int maxn=51;
const int maxm=100805;

int n,m;
double p[maxn][maxn][4];
double a[maxn*maxn][maxn*maxn],b[maxn*maxn];
int dir[][2]={{1,0},{0,1},{-1,0},{0,-1}};
int r,c;

double gaosi()
{
    for(int i=r-1;i>=0;i--){
        int down=max(0,i-m);
        for(int j=i-1;j>=down;j--){
            double temp=a[j][i]/a[i][i];
            for(int k=i;k>=down;k--){
                a[j][k]-=a[i][k]*temp;
            }
            b[j]-=b[i]*temp;
        }
    }
    return b[0]/a[0][0];
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF&&n&&m){
        for(int k=0;k<4;k++){
            for(int i=0;i<n;i++){
                for(int j=0;j<m;j++){
                    scanf("%lf",&p[i][j][k]);
                }
            }
        }
        r=n*m;
        memset(a,0,sizeof(a));
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                int id=i*m+j;
                b[id]=a[id][id]=1.0;
                if(i==n-1&&j==m-1){
                    a[id][id]=1.0;
                    continue;
                }
                for(int k=0;k<4;k++){
                    int x=i+dir[k][0];
                    int y=j+dir[k][1];
                    if(x<0||x>=n||y<0||y>=m){
                        continue;
                    }
                    a[id][x*m+y]=-p[i][j][k];
                }
            }
        }
        b[r-1]=0;
        printf("%.7lf\n",gaosi());
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值