KIDx的解题报告
1、地下迷宫
Description
由于山体滑坡,DK被困在了地下蜘蛛王国迷宫。为了抢在DH之前来到TFT,DK必须尽快走出此迷宫。此迷宫仅有一个出口,而由于大BOSS的力量减弱影响到了DK,使DK的记忆力严重下降,他甚至无法记得他上一步做了什么。所以他只能每次等概率随机的选取一个方向走。当然他不会选取周围有障碍的地方走。如DK周围只有两处空地,则每个都有1/2的概率。现在要求他平均要走多少步可以走出此迷宫。
Input
先是一行两个整数N, M(1<=N, M<=10)表示迷宫为N*M大小,然后是N行,每行M个字符,'.'表示是空地,'E’表示出口,'D’表示DK,'X’表示障碍。
Output
如果DK无法走出或要超过1000000步才能走出,输出tragedy!,否则输出一个实数表示平均情况下DK要走几步可以走出迷宫,四舍五入到小数点后两位。
Sample Input
1 2
ED
3 3
D.X
.X.
X.E
Sample Output:
1.00
tragedy!
解析:E[i]表示从i点到达终点的期望步数
设i的临近可达点有a1, a2, a3,...,an
E[i]可以随机选一个临近点再走到终点
则平均步数E[i] = ((E[a1]+1) + (E[a2]+1) + ... + (E[an]+1)) / n;
化简得n*E[i] - E[a1] - E[a2] - ... - E[an] = n
设s为起点,e为终点:
显然对于e点有方程:E[e] = 0
然后对每个点都建立一个方程然后高斯消元求出E[s]即可
#include <queue> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <algorithm> #include <iostream> using namespace std; #define M 101 #define eps 1e-9 int equ, var; double a[M][M], x[M]; int r, c, has[15][15], sx, sy, ex, ey; char map[15][15]; bool vis[15][15]; int xm[] = {-1, 0, 1, 0}; int ym[] = {0, 1, 0, -1}; int Gauss () { int i, j, k, col, max_r; for (k = 0, col = 0; k < equ && col < var; k++, col++) { max_r = k; for (i = k+1; i < equ; i++) if (fabs (a[i][col]) > fabs (a[max_r][col])) max_r = i; if (fabs (a[max_r][col]) < eps) return 0; //无解 if (k != max_r) { for (j = col; j < var; j++) swap (a[k][j], a[max_r][j]); swap (x[k], x[max_r]); } x[k] /= a[k][col]; for (j = col+1; j < var; j++) a[k][j] /= a[k][col]; a[k][col] = 1; for (i = 0; i < equ; i++) if (i != k) { x[i] -= x[k] * a[i][k]; for (j = col+1; j < var; j++) a[i][j] -= a[k][j] * a[i][col]; a[i][col] = 0; } } return 1; } struct point{ int x, y; }; void bfs () { int u, v, i, n, en = 1; memset (vis, false, sizeof(vis)); memset (has, -1, sizeof(has)); point ft, tp; ft.x = sx, ft.y = sy; queue<point> q; q.push (ft); //建立方程E(e) = 0 has[ex][ey] = 0; //设终点的方程编号为0 a[0][0] = 1, x[0] = 0; has[sx][sy] = en++; //设起点的方程编号为1 //bfs建立其他点的方程 while (!q.empty()) { ft = q.front(); q.pop(); if (map[ft.x][ft.y] == 'E') continue; //终点e的方程已经建立好 u = has[ft.x][ft.y]; n = 0; //n*E(u) - E(a1) - E(a2) -... = n for (i = 0; i < 4; i++) { tp.x = ft.x + xm[i]; tp.y = ft.y + ym[i]; if (tp.x < 0 || tp.y < 0 || tp.x >= r || tp.y >= c) continue; if (map[tp.x][tp.y] == 'X') continue; //邻近的点合法,得到该方程u变元v的系数 if (has[tp.x][tp.y] > -1) v = has[tp.x][tp.y]; else v = has[tp.x][tp.y] = en++; ++n; a[u][v] = -1; //已访问过的点不需要再去建立方程 if (vis[tp.x][tp.y]) continue; vis[tp.x][tp.y] = true; q.push (tp); } //u的系数以及方程右边的值 a[u][u] = x[u] = n; } equ = var = en; } int main() { int i, j; while (~scanf ("%d%d", &r, &c)) { for (i = 0; i < r; i++) scanf ("%s", map[i]); for (i = 0; i < r; i++) { for (j = 0; j < c; j++) { if (map[i][j] == 'D') sx = i, sy = j; else if (map[i][j] == 'E') ex = i, ey = j; } } //初始化 for (i = 0; i < M; i++) for (j = 0; j < M; j++) a[i][j] = 0; bfs (); if (Gauss()) printf ("%.2f\n", x[1]); else puts ("tragedy!"); } return 0; }
2、掷飞盘
Description
m个人位于正m边形的顶点上,彼此抛掷飞盘。他们共有两个飞盘,且开始时这两个飞盘位于相距为n的两个人的手中(相邻两个人相距为1,依此类推)。在每次抛掷时两个飞盘被同时抛出,飞盘都以1/2的概率被抛到掷飞盘的人左边相邻的人,1/2的概率被抛到右边相邻的人。此过程一直进行,直到两个飞盘被掷到同一个人手中,求此抛掷飞盘的游戏平均情况下(期望)会在抛掷几次后结束。
Input
每行有两个整数m (2<m<=100),n (0 < n < m)。
Output
对每组数据m,n,输出平均所需步数(四舍五入,保留两位小数),如果有限步内不可能结束就输出INF。
Sample Input
3 1
4 1
Sample Output
4.00
INF
解析:设E[n]是距离从n变到0的期望步数,那么显然有:E[0] = 0
由于距离变成n的组合有:左左(距离不变),右右(距离不变),左右(由n-2扩大而来),右左(由n+2缩小而来)
于是对于E[n]有:
E[n] = [(E[n]+1) + (E[n]+1) + (E[n-2]+1) + (E[n+2]+1)] / 4
化简得:2*E[n] - E[n-2] - E[n+2] = 4
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <algorithm> using namespace std; #define M 105 #define eps 1e-9 int equ, var; double a[M][M], x[M]; int Gauss () { int i, j, k, col, max_r; for (k = 0, col = 0; k < equ && col < var; k++, col++) { max_r = k; for (i = k+1; i < equ; i++) if (fabs (a[i][col]) > fabs (a[max_r][col])) max_r = i; if (fabs (a[max_r][col]) < eps) return 0; if (k != max_r) { for (j = col; j < var; j++) swap (a[k][j], a[max_r][j]); swap (x[k], x[max_r]); } x[k] /= a[k][col]; for (j = col+1; j < var; j++) a[k][j] /= a[k][col]; a[k][col] = 1; for (i = 0; i < equ; i++) if (i != k) { x[i] -= x[k] * a[i][k]; for (j = col+1; j < var; j++) a[i][j] -= a[k][j] * a[i][col]; a[i][col] = 0; } } return 1; } int has[M], k, f, m; //has[i] 存的是 i状态的方程号,f=m/2 int cal (int n) //得到合法的n,距离n不能小于0,也不能超过m的一半 { if (n < 0) n = -n; if (n > f) n = m - n; return n; } void dfs (int n) { n = cal (n); if (has[n] >= 0) return ; has[n] = k++; dfs (n-2); dfs (n+2); } int main () { int n, i, j; while (~scanf ("%d%d", &m, &n)) { f = m >> 1; n = cal (n); //输入可能是非法的距离n,trick memset (has, -1, sizeof(has)); k = 0; dfs (n); //dfs遍历得到所有可达状态,每个状态都建立一条方程 if (has[0] == -1) { puts ("INF"); continue; } //高斯消元的初始化 equ = var = k; for (i = 0; i < k; i++) for (j = 0; j < k; j++) a[i][j] = 0; //建图 a[has[0]][has[0]] = 1; x[has[0]] = 0; //E[0] = 0 for (i = 1; i <= f; i++) { //2*E[n] - E[n-2] - E[n+2] = 4 if (has[i] == -1) continue; //i状态不可达 int u = has[i], v; a[u][u] = 2; //E[n] v = has[cal(i+2)]; //E[n+2] --a[u][v]; v = has[cal(i-2)]; //E[n-2] --a[u][v]; x[u] = 4; //方程右边 } Gauss (); printf ("%.2f\n", x[has[n]]); //输出E[n] } return 0; }