题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4418
题目大意:
有n个点标号为0~n-1,A从x点出发每次可以走1~m步,走k步的概率为pk, 如果到达了n-1点,则往回走即(n-1~0),求走到y时走的步数的概率。
解题思路:
概率dp+高斯消元。
因为0~n-1中的点都有方向,为了是方向统一,可以增加n-2个点,使统一向一个方向走0~n-1~0
n-1右边的点表示向左走,两种不同的状态。
dp[i]表示从i开始到达终点的步数期望 dp[i]=(dp[i+1]+1)*p1+(dp[i+2]+2)*p2+(dp[i+3]+3)*p3.......。
注意有些点不能到达,所以先BFS找能够到达的点,对于不能到达的点直接输出Impossible
然后构建2*n-2元方程求解就行了。注意有些步数不能走。
最好把能够有效的状态单独拿出来,直接重新构建方程。
代码:
#include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<string> #include<cstring> #include<algorithm> #include<vector> #include<map> #include<set> #include<stack> #include<list> #include<queue> #define eps 1e-6 #define INF 0x1f1f1f1f #define PI acos(-1.0) #define ll __int64 #define lson l,m,(rt<<1) #define rson m+1,r,(rt<<1)|1 #pragma comment(linker, "/STACK:1024000000,1024000000") using namespace std; //freopen("data.in","r",stdin); //freopen("data.out","w",stdout); #define Maxn 210 int gap[Maxn],cnt,nn; int vis[Maxn],n,m,x,y,d; double pp[Maxn]; double g[Maxn][Maxn],xx[Maxn]; void bfs() { queue<int>myq; myq.push(x); vis[x]=true; while(!myq.empty()) { int tmp=myq.front(); myq.pop(); if(tmp==y||tmp==nn-y) //到了终点后就不能再走了 continue; for(int i=1;i<=cnt;i++) { int tt; tt=(tmp+gap[i])%nn; //统一转化成向一个方向走 if(vis[tt]) continue; vis[tt]=true; myq.push(tt); } } } void gg(int row,int col) //高斯消元 0~row 和0~col { for(int i=0,j=0;i<row,j<col;i++,j++) { int t=i; for(int k=i+1;k<=row;k++) if(fabs(g[k][j])>fabs(g[t][j])) t=k; if(fabs(g[t][j])<eps) continue; if(t-i) { for(int k=i;k<=col;k++) swap(g[i][k],g[t][k]); } for(int q=i+1;q<=row;q++) { if(fabs(g[q][j])<eps) //该系数已经变成了0 continue; double tmp=g[q][j]/g[i][j]; g[q][j]=0.0; for(int k=j+1;k<=col;k++) g[q][k]-=tmp*g[i][k]; } } for(int i=row;i>=0;i--) { if(fabs(g[i][i])<eps) //系数都为0了 continue; xx[i]=g[i][col]; for(int j=row;j>i;j--) xx[i]-=g[i][j]*xx[j]; xx[i]/=g[i][i]; } } int main() { int t,a; scanf("%d",&t); while(t--) { scanf("%d%d%d%d%d",&n,&m,&y,&x,&d); cnt=0; double sum=0.0; for(int i=1;i<=m;i++) { scanf("%d",&a); if(a) //可走 { gap[++cnt]=i; //能走的 pp[cnt]=a*1.0/100.0; sum+=pp[cnt]*i; } } //cout<<sum<<endl; if(x==y) //起点和终点为同一点 { printf("0.00\n"); continue; } memset(vis,false,sizeof(vis)); nn=2*n-2; if(d>0) x=nn-x; //两种不同的状态 bfs(); if(!vis[y]&&!vis[2*n-2-y]) //不能到达 { printf("Impossible !\n"); continue; } memset(g,0,sizeof(g)); for(int i=0;i<nn;i++) //枚举开始的位置,注意一共有nn个未知数 { if(!vis[i]) continue; //把不能到达的都清零,无效方程,没关系 g[i][i]+=1.0; if(i==y||i==nn-y) continue; for(int j=1;j<=cnt;j++) { int tt=(i+gap[j])%nn; g[i][tt]-=pp[j]; } g[i][nn]+=sum; } gg(nn-1,nn); printf("%0.2f\n",xx[x]); } return 0; }