HDU 4418 Time travel (概率,高斯消元)

题意: 有一个人在一根长度n的线段上来回走,方向是固定的,除非碰到线段的左右端点。他每次走的时候,有p1的概率走1步(即向前走1个点),p2的概率走2步...pm的概率走m步,让你求从点x走到点y的步数的期望值。若不可能走到,输出"Impossible !"。

解法: 对于来回走的处理方式,比如0 1 2 3 4 5,我们可以把他看成 0 1 2 3 4 5 4(6) 3(7) 2(8) 1(9),1和9代表点1,2和8代表点2...这样处理后就可以很容易的确定从当前位置pos走j步后的位置: newpos = (pos+j)mod(2*n-2)。设从点i到y的期望步数为E(i),那么有E(i) = sigma((E(i+j)+j)*p(j)),p(j)为走j步的概率,整理得E(i)-sigma(E(i+j)*p(j)) = sigma(j*p(j))。用广搜确定终点是否可达,然后建立n个方程,高斯消元即可。(关于网传必须只对可达点建立方程,本着不信邪的态度建了n个,AC了。。难道数据水?个人觉得建n个方程是没有问题的。)

/* Created Time: Thursday, October 31, 2013 PM07:18:43 CST */
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
using namespace std;
const double eps = 1e-8;
const int M = 210;
int n,m,dv,sv,MOD;
double p[M],g[M][M]; // g[][] : 方程组对应的增广矩阵
bool vis[M];
int sig(double x) { return (x>eps)-(x<-eps); }
double abs(double x) { return sig(x)<0?-x:x; }
bool check() {
    memset(vis,0,sizeof(vis));
    vis[sv] = true;
    queue <int> que;
    que.push(sv);
    while(!que.empty()) {
        int f = que.front(); que.pop();
        if (f==dv || f==2*n-2-dv) continue;
        for (int i = 1; i <= m; i ++) {
            if (sig(p[i])==0) continue;
            int e = (f+i)%MOD;
            if (vis[e]) continue;
            vis[e] = true;
            que.push(e);
        }
    }
    return vis[dv] || vis[2*n-2-dv];
}
void Gauss() {
    int k,col;
    int equ,var;
    equ = var = MOD;
    for (k=0,col=0; k<equ && col<var; k++,col++) {
        // 找到变量x(col)的系数最大行
        int maxr = k;
        for (int i = k+1; i < equ; i ++) 
            if (abs(g[i][col])>abs(g[maxr][col]))
                maxr = i;
        // 若变量x(col)不存在非0系数,则说明x(col)为自由元
        if (sig(g[maxr][col])==0) {
            k--;
            continue;
        }
        // 将系数最大行与当前行交换(据说这样能避免一些精度问题)
        if (maxr!=k)
            for (int i = col; i <= var; i ++)
                swap(g[k][i],g[maxr][i]);
        // 将k+1行到equ-1行的x(col)消去
        for (int i = k+1; i < equ; i ++) {
            double coe = g[i][col]/g[k][col];
            for (int j = col; j <= var; j ++) {
                g[i][j] -= g[k][j]*coe;
            }
        }
    }
    // 回代
    for (k--,col--; k>=0 && col>=0; k--,col--) {
        while (sig(g[k][col])==0) col--;
        g[k][var] /= g[k][col];
        for (int i = k-1; i >= 0; i --) {
            g[i][var] -= g[i][col]*g[k][var];
            g[i][col] = 0;
        }
    }
}
void work() {
    int tp = 2*n-2-dv;
    // 建立增广矩阵
    for (int i = 0; i < MOD; i ++) {
        g[i][i] = 1;
        for (int j = 1; j <= m; j ++) {
            g[i][(i+j)%MOD] -= p[j];
            g[i][MOD] += p[j]*j;
        }
    }
    for (int i = 0; i <= MOD; i ++)
        g[dv][i] = g[tp][i] = 0;
    g[dv][dv] = g[tp][tp] = 1;

    Gauss();
    for (int i = 0; i < MOD; i ++) 
        if (sig(g[i][sv])!=0) {
            printf("%.2f\n",g[i][MOD]);
            break;
        }
}
int main() {
    int cas,d;
    scanf("%d",&cas);
    while (cas--) {
        scanf("%d%d%d%d%d",&n,&m,&dv,&sv,&d);
        for (int i = 1; i <= m; i ++) {
            scanf("%lf",&p[i]);
            p[i] /= 100;
        }
        // 需要特判起点等于终点的情况,不然WA死,哪怕在解方程后特判0.00都不管用。
        if (sv==dv) {
            printf("0.00\n");
            continue;
        }
        MOD = 2*n-2;
        for (int i = 0; i < MOD; i ++)
            for (int j = 0; j <= MOD; j ++)
                g[i][j] = 0;
        if (d==1) sv = 2*n-sv-2;
        if (!check()) { 
            puts("Impossible !");
        }
        else 
            work();
    }
    return 0;
}
/*

3
19 10 1 17 1
12 4 3 7 31 4 1 0 22 16
20 6 10 7 0
22 36 0 6 21 15
4 2 2 0 -1
0 100

ans :
97.24
52.02
2.00

*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值