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

时间:2022-11-16 08:04:19

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


题意:一个人在数轴上来回走,以pi的概率走i步i∈[1, m],给定n(数轴长度),m,e(终点),s(起点),d(方向),求从s走到e经过的点数期望


参考博客:http://972169909-qq-com.iteye.com/blog/1689107

http://blog.csdn.net/sr_19930829/article/details/38959149


高斯消元求概率的题目,主要就是方向的转化,很巧妙。

对于概率的叠加不是特别理解,还得多思考。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <queue>
#include <algorithm>
#include <math.h>
using namespace std;
#define M 205
#define eps 1e-8
int equ, var;
double a[M][M], x[M];

int has[M], vis[M], k, e, n, m;
double p[M], sum;
  
bool free_x[M];  

int sgn(double x) {    
    return (x>eps)-(x<-eps);    
} 

int gauss()  
{  
    equ=k,var=k;  
    int i,j,k;  
    int max_r;
    int col; 
    double temp;  
    int free_x_num;  
    int free_index;   
    col=0; 
    memset(free_x,true,sizeof(free_x));  
    for(k=0;k<equ&&col<var;k++,col++)  
    {  
        max_r=k;  
        for(i=k+1;i<equ;i++)  
        {  
            if(sgn(fabs(a[i][col])-fabs(a[max_r][col]))>0)  
                max_r=i;  
        }  
        if(max_r!=k)  
        { 
            for(j=k;j<var+1;j++)  
                swap(a[k][j],a[max_r][j]);  
        }  
        if(sgn(a[k][col])==0)  
        { 
            k--; continue;  
        }  
        for(i=k+1;i<equ;i++)  
        { 
            if (sgn(a[i][col])!=0)  
            {  
                temp=a[i][col]/a[k][col];  
                for(j=col;j<var+1;j++)  
                {  
                    a[i][j]=a[i][j]-a[k][j]*temp;  
                }  
            }  
        }  
    }  
  
    for(i=k;i<equ;i++)  
    {   
        if (sgn(a[i][col])!=0)  
            return 0;  
    }  
    if(k<var)  
    {  
        for(i=k-1;i>=0;i--)  
        {  
            free_x_num=0;  
            for(j=0;j<var;j++)  
            {  
                if (sgn(a[i][j])!=0&&free_x[j])  
                    free_x_num++,free_index=j;  
            }  
            if(free_x_num>1) continue;  
            temp=a[i][var];  
            for(j=0;j<var;j++)  
            {  
                if(sgn(a[i][j])!=0&&j!=free_index)  
                    temp-=a[i][j]*x[j];  
            }  
            x[free_index]=temp/a[i][free_index];  
            free_x[free_index]=0;  
        }  
        return var-k;  
    }  
  
    for (i=var-1;i>=0;i--)  
    {  
        temp=a[i][var];  
        for(j=i+1;j<var;j++)  
        {  
            if(sgn(a[i][j])!=0)  
                temp-=a[i][j]*x[j];  
        }  
        x[i]=temp/a[i][i];  
    }  
    return 1;  
} 


int bfs (int u)
{
	memset (has, -1, sizeof(has));
	memset (a, 0, sizeof(a));
	memset (vis, 0, sizeof(vis));
	int v, i, flg = 0;
	queue<int> q;
	q.push (u);
	k = 0;
	has[u] = k++;
	while (!q.empty ())
	{
		u = q.front ();
		q.pop ();
		if (vis[u]) continue;
		vis[u] = 1;
		a[has[u]][has[u]] = 1;
		if (u == e || u == n-e)	
		{
			x[has[u]] = 0;
			flg = 1;
			continue;
		}
		x[has[u]] = sum;
		for (i = 1; i <= m; i++)
		{
			if (fabs (p[i]) < eps) continue;  //概率为0不构造 
			v = (u + i) % n;
			if (has[v] == -1) has[v] = k++;
			a[has[u]][has[v]] -= p[i];   //一个点可能多次访问,所以要叠加。不太理解。 
			q.push (v);
		}
	}
	for(int i = 0; i < k; i++) a[i][k] = x[i];
	return flg;
}

int main()
{
	int t, s, d, i;
	scanf ("%d", &t);
	while (t--)
	{
		scanf ("%d%d%d%d%d", &n, &m, &e, &s, &d);
		n = 2*(n-1);
		sum = 0;
		for (i = 1; i <= m; i++)
		{
			scanf ("%lf", p+i);
			p[i] = p[i] / 100;
			sum += p[i] * i;
		}
		if (s == e)
		{
			puts ("0.00");
			continue;
		}
		if (d > 0) s = (n - s) % n;
		if (!bfs (s))
		{
			puts ("Impossible !");
			continue;
		}
		gauss ();
		printf ("%.2f\n", x[has[s]]);
	}
    return 0;
}