HDU2262 Where is the canteen 数学期望 高斯消元

HDU2262 Where is the canteen

题目链接:[http://acm.hdu.edu.cn/showproblem.php?pid=2262]

Time Limit: 10000/5000 MS (Java/Others)Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 2364Accepted Submission(s): 772

Problem Description
After a long drastic struggle with himself, LL decide to go for some snack at last. But when steping out of the dormitory, he found a serious problem : he can’t remember where is the canteen… Even worse is the campus is very dark at night. So, each time he move, he check front, back, left and right to see which of those four adjacent squares are free, and randomly walk to one of the free squares until landing on a canteen.

Input
Each case begin with two integers n and m ( n<=15,m<=15 ), which indicate the size of the campus. Then n line follow, each contain m characters to describe the map. There are 4 different type of area in the map:
‘@’ is the start location. There is exactly one in each case.
‘#’ is an impassible square.
‘$’ is a canteen. There may be more than one in the campus.
‘.’ is a free square.

Output
Output the expected number of moves required to reach a canteen, which accurate to 6 fractional digits. If it is impossible , output -1.

题目大意:

LL在一个 n * m 的学校里,现在从宿舍出发,每次随机向四个方向中可以移动的一个方向移动一个单位,问到达食堂需要的步数的数学期望是多少(学校里有多个食堂),如果到达不了输出-1
’ @ ’ 表示宿舍
’ # ’ 表示障碍物
’ $ ’ 表示食堂
’ . ’ 表示空地

分析:

DP[ i ]表示 i 点到食堂的步数的数学期望值
不难得出概率DP的状态转移方程:
D P [ i ] = D P [ j 1 ] + ⋯ + D P [ j t ] t + 1 DP[i]=\frac {DP[j_1]+\cdots+DP[j_t]}t+1 DP[i]=tDP[j1]++DP[jt]+1

其中j1、……jt为可以从 i 直接到达的点,并且如果 j 点是食堂的话DP[ j ] = 0
将上述状态转移方程形式改变一下变成线性方程:
t ⋅ D P [ i ] − D P [ j 1 ] − ⋯ − D P [ j t ] = t t\cdot DP[i]-DP[j_1]-\cdots-DP[j_t]=t tDP[i]DP[j1]DP[jt]=t
所以首先用BFS找出连通图,也就是可以到达的所有点
对于每一个可以到达的点都可以得到一个线性方程
组合在一起得到一个线性方程组
高斯消元对线性方程组进行处理就可以得到答案了

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
static const int maxn = 20;
static const int MAXN = 400;
static const int dir[4][2] = {{1,0},{0,1},{-1,0},{0,-1}};
int n,m,sx,sy,tn;
char mp[maxn][maxn];        //记录campus的图
int d[MAXN];                //可以走的方向数
int dmap[MAXN];             //可行点位置标号对应的序号
double gauss[MAXN][MAXN];   //矩阵 用于消元
vector<int> v[MAXN];        //下一步可以走到的位置
int pos(int x,int y){
    int pos = (x-1)*m+y;
    return pos;
}
//pos用于算出位置标号
bool bfs(){
    tn = 0;									//可行点总数
    bool ok = false;                        //判断是否能走到食堂
    int used[maxn][maxn];                   //bfs标记
    memset(used,0,sizeof(used));
    for(int i=0;i<MAXN;i++) v[i].clear();
    queue<pair<int,int> > q;
    used[sx][sy] = 1;
    q.push(make_pair(sx,sy));
    while(!q.empty()){
        int nowx = q.front().first;
        int nowy = q.front().second;
        dmap[pos(nowx,nowy)] = ++tn;        //记录可行点位置序号
        int tmpd = 0;                       //可走的方向数
        q.pop();
        for(int i=0;i<4;i++){
            int nextx = nowx + dir[i][0];
            int nexty = nowy + dir[i][1];
            if(nextx<=n&&nextx>=1&&nexty<=m&&nexty>=1){
                if(mp[nextx][nexty]=='.'){
                    v[dmap[pos(nowx,nowy)]].push_back(pos(nextx,nexty));
                    tmpd++;
                    if(!used[nextx][nexty]){
                        q.push(make_pair(nextx,nexty));
                        used[nextx][nexty] = 1;
                    }
                }
                else if(mp[nextx][nexty]=='$'){
                    ok = true;
                    tmpd++;
                }
            }
        }
        d[dmap[pos(nowx,nowy)]] = tmpd;
    }
    return ok;
}
int main(){
    while(scanf("%d %d",&n,&m)!=EOF){
        memset(d,0,sizeof(d));
        memset(gauss,0,sizeof(gauss));
        for(int i=1;i<=n;i++){
            getchar();
            for(int j=1;j<=m;j++){
                mp[i][j] = getchar();
                if(mp[i][j]=='@'){
                    sx = i;
                    sy = j;
                    mp[i][j] = '.';
                }
            }
        }
        if(!bfs()){
            printf("-1\n");
            continue;
        }

        //gauss_jordan:
        for(int i=1;i<=tn;i++){
            gauss[i][i] = d[i];
            for(int j=0;j<v[i].size();j++){
                gauss[i][dmap[v[i][j]]] = -1;
            }
            gauss[i][tn+1] = d[i];
        }
        for(int i=1;i<tn;i++){
            for(int j=i+1;j<=tn;j++){
                double k = gauss[j][i]/gauss[i][i];
                for(int t=i;t<=tn+1;t++){
                    gauss[j][t] -= k * gauss[i][t];
                }
            }
        }
        bool ok = false;
        for(int i=tn;i>1;i--){
            if(gauss[i][i]==0){
                ok = true;
                printf("-1\n");
                break;
            }
            for(int j=i-1;j>=1;j--){
                double k = gauss[j][i]/gauss[i][i];
                gauss[j][i] = 0;
                gauss[j][tn+1] -= k * gauss[i][tn+1];
            }
        }
        if(ok) continue;
        printf("%.6f\n",gauss[dmap[pos(sx,sy)]][tn+1]/gauss[dmap[pos(sx,sy)]][dmap[pos(sx,sy)]]);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值