• POJ 3756 Chess Game [期望+高斯消元]


      这个模型跟小时候玩的大富翁棋类游戏差不多,起点是0,终点是N,每次掷骰子走1~6步,格子分为3种,前进1~6格,后退1~6格,以及停止一回合,求到达终点所需回合数的期望。

      用E(I)表示从第I个格子走到终点所需回合的期望,E(N)=0,转移方程为 E(I)=sum(1/6*(E(K1)+1))+sum(1/6*(E(K2)+2))。K1,K2都是从I掷1~6能走到的格子,如果这个格子有操作,就要跳到操作后的格子,第一个sum里求的是不会停一回合的期望和,第二个sum里求的是走到停一回合格子的期望和。

      因为有后退操作,所以转移是有环的,不能用记忆化搜索,要用高斯消元来做。

      一开始可以DFS一遍标记所以能到达的格子,这样可以直接判断是否有解。对于不可达的点,也不需要去消元。

      比较蛋疼的是遇到端点折回的情况,比如在N=2时,在1掷了个5,那路线就是1->2->1->0->1->2。。没总结出用什么式子算,索性写了个暴力模拟一直调整直到到达0~N的范围中。。

      这题精度比较低,暴力的循环DP1000次貌似也能过。。我本机随机数据测了一下,暴力DP的精度还是有限的,2位的精度基本都对上了,但位数再高一点精度就差不少了,所以这类题最好还是高斯消元解吧,反正也不麻烦。

      

      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <math.h>
      4 #include <algorithm>
      5 #define MAXN 105
      6 #define INF 0x3f3f3f3f
      7 #define eps 1e-9
      8 using namespace std;
      9 int n, nf, tx, ty;
     10 int reach[MAXN], fp[MAXN], id[MAXN], ids;
     11 double x[MAXN][MAXN], ans[MAXN];
     12 double gauss(int n,int m) {
     13     int row = 0, col = 0;
     14     for (;row < n && col < m; col++) {
     15         int maxr = row;
     16         for (int i = row+1; i < n; i++)
     17             if (fabs(x[i][col]) > fabs(x[maxr][col]))maxr = i;
     18         if (fabs(x[maxr][col]) < eps) continue;
     19         if (maxr != row) for (int i = 0; i <= m; i++) swap(x[row][i], x[maxr][i]);
     20         for (int k1 = row+1; k1 < n; k1++) {
     21             double div = x[k1][col] / x[row][col];
     22             for (int k2 = col; k2 <= m; k2++) {
     23                 x[k1][k2] -= div * x[row][k2];
     24             }
     25         }
     26         row++;
     27     }
     28     memset(ans, 0, sizeof ans);
     29     for (int i = n-1; i >= 0; i--) {
     30         double tmp = 0;
     31         for (int j = i+1; j < m; j++)
     32             tmp += ans[j] * x[i][j];
     33         ans[i] = (- x[i][m] - tmp) / x[i][i];
     34     }
     35     return ans[0];
     36 }
     37 int getnp(int p,int i) {
     38     int np = i+p;
     39     while (np < 0 || np > n) np = np > n ? 2*n-np : -np;
     40     return np;
     41 }
     42 void dfsreach(int p) {
     43     reach[p] = 1;
     44     for (int i = 1;i <= 6; i++) {
     45         int np = getnp(p, i);
     46         if (fp[np] != INF) np = getnp(np, fp[np]);
     47         if (!reach[np]) dfsreach(np);
     48     }
     49 }
     50 void solve() {
     51     memset(reach, 0 ,sizeof reach);
     52     dfsreach(0);
     53     if (reach[n] == 0) {
     54         printf("Impossible\n");
     55         return;
     56     }
     57     ids = 0;
     58     double one = 1.0/6.0;
     59     for (int i = 0; i < n; i++) if (reach[i]) id[i] = ids++;
     60     memset(x, 0, sizeof x);
     61     for (int p = 0; p < n; p++) {
     62         if (reach[p] == 0) continue;
     63         x[id[p]][id[p]] = -1;
     64         for (int i = 1; i <= 6; i++) {
     65             int np = getnp(p, i), nfp = fp[np];
     66             if (fp[np] != INF) np = getnp(np, fp[np]);
     67             //已经到达终点
     68             if (np == n) {
     69                 x[id[p]][ids] += one;
     70             //注意,是第一步跳到了停止上才是停止
     71             } else if(nfp == 0) {
     72                 x[id[p]][id[np]] += one;
     73                 x[id[p]][ids] += one * 2;
     74             } else {
     75                 x[id[p]][id[np]] += one;
     76                 x[id[p]][ids] += one;
     77             }
     78         }
     79     }
     80     double x = gauss(ids, ids);
     81     printf("%.2f\n", x);
     82 }
     83 int main(){
     84     freopen("test.in","r",stdin);
     85     freopen("test.out","w",stdout);
     86     while (scanf("%d", &n) != EOF) {
     87         scanf("%d", &nf);
     88         memset(fp, 0x3f, sizeof fp);
     89         for (int i = 0; i < nf; i++)
     90             scanf("%d%d", &tx, &ty), fp[tx] = ty;
     91         scanf("%d", &nf);
     92         for (int i = 0; i < nf; i++)
     93             scanf("%d%d", &tx, &ty), fp[tx] = -ty;
     94         scanf("%d", &nf);
     95         for (int i = 0; i < nf; i++)
     96             scanf("%d", &tx), fp[tx] = 0;
     97         solve();
     98     }
     99     return 0;
    100 }

      

  • 相关阅读:
    Java中符号位扩展
    BZOJ2754: [SCOI2012]喵星球上的点名(AC自动机)
    BZOJ1030: [JSOI2007]文本生成器(AC自动机)
    BZOJ2434: [Noi2011]阿狸的打字机(AC自动机 树状数组)
    BZOJ1432: [ZJOI2009]Function(找规律)
    BZOJ2659: [Beijing wc2012]算不出的算式(数学)
    洛谷P3796 【模板】AC自动机(加强版)
    洛谷P3966 [TJOI2013]单词(AC自动机)
    BZOJ2580: [Usaco2012 Jan]Video Game(AC自动机)
    后缀自动机经典操作
  • 原文地址:https://www.cnblogs.com/swm8023/p/2696690.html
Copyright © 2020-2023  润新知