HDOJ 4418 Time travel (bfs + 高斯消元)



点击打开链接


题意:有一根编号为0~n-1的数轴,起点编号为x,终点编号为y,从起点开始,每次可以走1~m步中的任意步数,如果走到头就返回。如果d为1,那么一开始的走的方向是逆着数轴的,如果d为0,那么一开始走的方向是顺着数轴的。特别的,如果起点是0或者n-1,那么方向一定是确定的,所以d=-1.问从起点到终点的期望步数。

这道题有一个折返走的特殊现象,为了使得操作简便,可以把这根数轴再虚拟出一部分,0~n-1是原来的部分,n-2~1(n>2)。如果1<=n<=2,就不用虚拟了。现在讨论一下一般情况,如果d=1,那么把起点改为2*n-2-x,如果d=0或者-1,那么起点还是x.这样的话,只要一直往顺着数轴的方向走,就相当于折返走。如果当前位置是u,i为要走的步数,那么下一个位置就是(u+i)%(2*n-2) 。再讨论特殊情况,如果n=2,也是满足之前一般情况的式子的。当n=1时,会出现mod 0的情况,所以在一开始就要特判。

注意因为p[i]可能等于0,所以会出现某些点到不了的情况,所以先用bfs确定那些到不了的点,那些点对应的未知数一定是无解的。如果不把这些无解的挑出来,就不能求出其他的唯一解的情况,因为Gauss过程中会跳出。注意由于进行了虚拟数轴,所以可能有两个终点,如果两个终点都达到不了就输出impossible,并且把两个终点都标上已经求出解为0。

两个bug。一个是没有在bfs过程中判断p[i]=0时,不能扩展出一个新的结点。第二个就是后面对于从同一个位置出发,可能在走不同步数以后会到达同一个点。


dp[i]表示走到该点到达终点还需要的步数的期望。

dp[i]=∑pk/100*(dp[i+k]+k)。



[cpp]  view plain  copy
  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<cmath>  
  4. #include<algorithm>  
  5. #include<queue>  
  6. #define VN 300  
  7. #define eps 1e-8  
  8. using namespace std;  
  9. int n, m, d, x, y, u, v, T, N;  
  10. queue <int> q;  
  11. double a[VN][VN], b[VN], p[VN];  
  12. int flag[VN];  
  13. void Gauss(int n)  
  14. {  
  15.     int i,j,k,r;  
  16.     for(i=0;i<n;++i)  
  17.     {  
  18.         //数据稳定性优化  
  19.         r = i;  
  20.         for(j=i+1;j<n;++j) if(fabs(a[j][i]) > fabs(a[r][i])) r=j;  
  21.         if(fabs(a[r][i]) < eps) continue;  
  22.         if(r!=i)  
  23.         {  
  24.             for(j=0;j<n;++j)swap(a[r][j],a[i][j]);  
  25.             swap(b[r],b[i]);  
  26.         }  
  27.         //消元  
  28.         for(j=n;j>=i;j--)  
  29.         for(k=i+1;k<n;++k)  
  30.             if(j==n) b[k]-=a[k][i]/a[i][i]*b[i];  
  31.             else a[k][j]-=a[k][i]/a[i][i]*a[i][j];  
  32.     }  
  33.     //回代  
  34.     for(i=n-1;i>=0;--i)  
  35.     {  
  36.         if(fabs(a[i][i]) < eps) continue;  
  37.         for(j=i+1;j<n;++j)b[i]-=a[i][j]*b[j];  
  38.         b[i]/=a[i][i];  
  39.     }  
  40. }  
  41. void bfs()  
  42. {  
  43.     memset(flag, 0, sizeof(flag));  
  44.     while (!q.empty()) q.pop();  
  45.     if (d == 0 || d == -1) u = x;  
  46.     else u = 2*n-x-2;  
  47.     q.push(u);  
  48.     flag[u] = 1;  
  49.     while (!q.empty())  
  50.     {  
  51.         u = q.front();  
  52.         for (int i = 1; i <= m; i++)  
  53.             {  
  54.                 if (p[i] < eps) continue;//bug1  
  55.                 v = (u+i) % (2*n-2);  
  56.                 if (flag[v] == 0)  
  57.                 {  
  58.                     flag[v] = 1;  
  59.                     q.push(v);  
  60.                 }  
  61.             }  
  62.         q.pop();  
  63.     }  
  64. }  
  65. int main()  
  66. {  
  67.     scanf("%d", &T);  
  68.     while (T--)  
  69.     {  
  70.         scanf("%d%d%d%d%d", &n, &m, &y, &x, &d);  
  71.         for (int i = 1; i <= m; i++) scanf("%lf", &p[i]);  
  72.         if (n == 1)  
  73.         {  
  74.             printf("0.00\n");  
  75.             continue;  
  76.         }  
  77.         bfs();  
  78.         memset(a, 0, sizeof(a));  
  79.         memset(b, 0, sizeof(b));  
  80.         N = 2*n-2;  
  81.         for (int i = 0; i < N; i++)  
  82.             if (flag[i] == 0) b[i] = 1;  
  83.         for (int i = 0; i < N; i++)  
  84.             if (flag[i] != 0 && i != y && i !=(2*n-2-y))  
  85.         {  
  86.             a[i][i] = 1;  
  87.             for (int j = 1; j <= m; j++)  
  88.             {  
  89.                 a[i][(i+j) % N] += -p[j]/100.0; //bug2  
  90.                 b[i] += p[j]/100.0*j;  
  91.             }  
  92.         }  
  93.         a[y][y] = 1;  
  94.         a[2*n-2-y][2*n-2-y] = 1;  
  95.         if (flag[y] == 1 || flag[2*n-2-y] == 1)  
  96.         {  
  97.             Gauss(N);  
  98.             if (d == 0 || d == -1) printf("%.2f\n", b[x]);  
  99.             else printf("%.2f\n", b[2*n-x-2]);  
  100.         }  
  101.         else printf("Impossible !\n");  
  102.     }  
  103.     return 0;  
  104.   
  105. }  

点击打开链接


题意:有一根编号为0~n-1的数轴,起点编号为x,终点编号为y,从起点开始,每次可以走1~m步中的任意步数,如果走到头就返回。如果d为1,那么一开始的走的方向是逆着数轴的,如果d为0,那么一开始走的方向是顺着数轴的。特别的,如果起点是0或者n-1,那么方向一定是确定的,所以d=-1.问从起点到终点的期望步数。

这道题有一个折返走的特殊现象,为了使得操作简便,可以把这根数轴再虚拟出一部分,0~n-1是原来的部分,n-2~1(n>2)。如果1<=n<=2,就不用虚拟了。现在讨论一下一般情况,如果d=1,那么把起点改为2*n-2-x,如果d=0或者-1,那么起点还是x.这样的话,只要一直往顺着数轴的方向走,就相当于折返走。如果当前位置是u,i为要走的步数,那么下一个位置就是(u+i)%(2*n-2) 。再讨论特殊情况,如果n=2,也是满足之前一般情况的式子的。当n=1时,会出现mod 0的情况,所以在一开始就要特判。

注意因为p[i]可能等于0,所以会出现某些点到不了的情况,所以先用bfs确定那些到不了的点,那些点对应的未知数一定是无解的。如果不把这些无解的挑出来,就不能求出其他的唯一解的情况,因为Gauss过程中会跳出。注意由于进行了虚拟数轴,所以可能有两个终点,如果两个终点都达到不了就输出impossible,并且把两个终点都标上已经求出解为0。

两个bug。一个是没有在bfs过程中判断p[i]=0时,不能扩展出一个新的结点。第二个就是后面对于从同一个位置出发,可能在走不同步数以后会到达同一个点。


dp[i]表示走到该点到达终点还需要的步数的期望。

dp[i]=∑pk/100*(dp[i+k]+k)。



[cpp]  view plain  copy
  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<cmath>  
  4. #include<algorithm>  
  5. #include<queue>  
  6. #define VN 300  
  7. #define eps 1e-8  
  8. using namespace std;  
  9. int n, m, d, x, y, u, v, T, N;  
  10. queue <int> q;  
  11. double a[VN][VN], b[VN], p[VN];  
  12. int flag[VN];  
  13. void Gauss(int n)  
  14. {  
  15.     int i,j,k,r;  
  16.     for(i=0;i<n;++i)  
  17.     {  
  18.         //数据稳定性优化  
  19.         r = i;  
  20.         for(j=i+1;j<n;++j) if(fabs(a[j][i]) > fabs(a[r][i])) r=j;  
  21.         if(fabs(a[r][i]) < eps) continue;  
  22.         if(r!=i)  
  23.         {  
  24.             for(j=0;j<n;++j)swap(a[r][j],a[i][j]);  
  25.             swap(b[r],b[i]);  
  26.         }  
  27.         //消元  
  28.         for(j=n;j>=i;j--)  
  29.         for(k=i+1;k<n;++k)  
  30.             if(j==n) b[k]-=a[k][i]/a[i][i]*b[i];  
  31.             else a[k][j]-=a[k][i]/a[i][i]*a[i][j];  
  32.     }  
  33.     //回代  
  34.     for(i=n-1;i>=0;--i)  
  35.     {  
  36.         if(fabs(a[i][i]) < eps) continue;  
  37.         for(j=i+1;j<n;++j)b[i]-=a[i][j]*b[j];  
  38.         b[i]/=a[i][i];  
  39.     }  
  40. }  
  41. void bfs()  
  42. {  
  43.     memset(flag, 0, sizeof(flag));  
  44.     while (!q.empty()) q.pop();  
  45.     if (d == 0 || d == -1) u = x;  
  46.     else u = 2*n-x-2;  
  47.     q.push(u);  
  48.     flag[u] = 1;  
  49.     while (!q.empty())  
  50.     {  
  51.         u = q.front();  
  52.         for (int i = 1; i <= m; i++)  
  53.             {  
  54.                 if (p[i] < eps) continue;//bug1  
  55.                 v = (u+i) % (2*n-2);  
  56.                 if (flag[v] == 0)  
  57.                 {  
  58.                     flag[v] = 1;  
  59.                     q.push(v);  
  60.                 }  
  61.             }  
  62.         q.pop();  
  63.     }  
  64. }  
  65. int main()  
  66. {  
  67.     scanf("%d", &T);  
  68.     while (T--)  
  69.     {  
  70.         scanf("%d%d%d%d%d", &n, &m, &y, &x, &d);  
  71.         for (int i = 1; i <= m; i++) scanf("%lf", &p[i]);  
  72.         if (n == 1)  
  73.         {  
  74.             printf("0.00\n");  
  75.             continue;  
  76.         }  
  77.         bfs();  
  78.         memset(a, 0, sizeof(a));  
  79.         memset(b, 0, sizeof(b));  
  80.         N = 2*n-2;  
  81.         for (int i = 0; i < N; i++)  
  82.             if (flag[i] == 0) b[i] = 1;  
  83.         for (int i = 0; i < N; i++)  
  84.             if (flag[i] != 0 && i != y && i !=(2*n-2-y))  
  85.         {  
  86.             a[i][i] = 1;  
  87.             for (int j = 1; j <= m; j++)  
  88.             {  
  89.                 a[i][(i+j) % N] += -p[j]/100.0; //bug2  
  90.                 b[i] += p[j]/100.0*j;  
  91.             }  
  92.         }  
  93.         a[y][y] = 1;  
  94.         a[2*n-2-y][2*n-2-y] = 1;  
  95.         if (flag[y] == 1 || flag[2*n-2-y] == 1)  
  96.         {  
  97.             Gauss(N);  
  98.             if (d == 0 || d == -1) printf("%.2f\n", b[x]);  
  99.             else printf("%.2f\n", b[2*n-x-2]);  
  100.         }  
  101.         else printf("Impossible !\n");  
  102.     }  
  103.     return 0;  
  104.   
  105. }  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值