概率dp(求期望)+高斯消元 hdu-4418-Time travel

来源:互联网 发布:c语言书籍推荐 编辑:程序博客网 时间:2024/05/17 06:35

题目链接:

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 210int 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;}