UVA 10531 Maze Statistics 迷宫统计 迷宫插头DP 四联通 概率

  题意:

    有一个N*M的图,每个格子有独立概率p变成障碍物。你要从迷宫左上角走到迷宫右下角。求每个格子成为一个有解迷宫中的障碍物的概率。N <= 5,M <= 6

  分析:

    这真是一道好题,网上几乎没有任何关于四连通的插头DP的任何资料,这道题目很好地反映了这类问题。

    四连通中,只要你存在了右插头,必然存在下插头,当然,你的插头不一定需要真正连到可行格子中,因此在当前行中你只需要记录右插头。

    但是不是需要换行的吗?由于下插头跟右插头是同时存在的,那么我们只需要把右插头当做下插头来用就可以了,是不是比普通的简单路径的插头dp简单很多?

    来,我们看一下转移吧,其实情况的分类还是跟普通的插头dp一样的,不得不说cdq的插头普适性真是厉害。

    1、同时存在左插头和上插头,那么只需要把连通块连一下,然后记一个右插头。

    2、只存在左插头或者上插头,那么只需要延续连通块,再记一个右插头。

    3、都不存在左插头和上插头,那么只需要新建一个连通块,记在右插头。

    而换行操作,只需要把右插头变成下插头就可以了。

  程序:(学习了某岛大神的代码之后才写出来了这份代码)

  1 #include <cstdio>
  2 #include <cstdlib>
  3 #include <cstring>
  4 #include <string>
  5 #include <algorithm>
  6 #include <iostream>
  7 
  8 using namespace std;
  9 
 10 typedef long long LL;
 11 const int HASH = 30007;
 12 const int STATE = 1000010;
 13 const int MAXD = 15;
 14 const double EPS = 1e-15;
 15 int n, m;
 16 double maze[MAXD][MAXD];
 17 int code[MAXD], ch[MAXD];
 18 
 19 int fcmp(double x)
 20 {
 21     return x < -EPS ? -1 : x > EPS;
 22 }
 23 
 24 void decode(LL st)
 25 {
 26     int i;
 27     for (i = m; i >= 0; --i)
 28     {
 29         code[i] = st&7;
 30         st >>= 3;
 31     }
 32 }
 33 
 34 LL encode()
 35 {
 36     LL st = 0;
 37     int i, cnt = 1;
 38     memset(ch, -1, sizeof(ch));
 39     ch[0] = 0, ch[1] = 1;
 40     for (i = 0; i <= m; ++i)
 41     {
 42         if (ch[code[i]] == -1)
 43             ch[code[i]] = ++cnt;
 44         code[i] = ch[code[i]];
 45         st <<= 3;
 46         st |= code[i];
 47     }
 48     return st;
 49 }
 50 
 51 struct HASHMAP
 52 {
 53     int head[HASH], next[STATE], size;
 54     LL state[STATE];
 55     double f[STATE];
 56     void clear()
 57     {
 58         size = 0;
 59         memset(head, -1, sizeof(head));
 60     }
 61     void push(LL st, double ans)
 62     {
 63         int i;
 64         decode(st);
 65         for (i = 0; i <= m; ++i)
 66             if (code[i] == 1)
 67                 break ;
 68         if (i == m+1) //如果编号为1的连通块不存在,那就不可行
 69             return ;
 70         int x = st%HASH;
 71         for (i = head[x]; i != -1; i = next[i])
 72             if (st == state[i])
 73             {
 74                 f[i] += ans;
 75                 return ;
 76             }
 77         f[size] = ans;
 78         state[size] = st;
 79         next[size] = head[x];
 80         head[x] = size ++;
 81     }
 82 }hm[2];
 83 
 84 void in()
 85 {
 86     int i, j;
 87     scanf("%d %d", &n, &m);
 88     for (i = 1; i <= n; ++i)
 89         for (j = 1; j <= m; ++j)
 90             scanf("%lf", &maze[i][j]);
 91 }
 92 
 93 void dp_blank(int i, int j, int cur)
 94 {
 95     if (!fcmp(1.0-maze[i][j]))
 96         return ;
 97     int k, lef, up, t;
 98     for (k = 0; k < hm[cur].size; ++k)
 99     {
100         if (!fcmp(hm[cur].f[k]))
101             continue ;
102         decode(hm[cur].state[k]);
103         lef = code[j-1], up = code[j];
104         if (lef && up)//分类讨论只对右插头做修改
105         {
106             if (lef != up)
107             {
108                 if (lef < up)
109                     swap(lef, up);
110                 for (t = 0; t <= m; ++t)
111                     if (code[t] == lef)
112                         code[t] = up;
113             }
114         }
115         else
116         {
117             if (lef || up)
118                 code[j] = lef|up;
119             else
120                 code[j] = m+1;
121         }
122         hm[cur^1].push(encode(), hm[cur].f[k]*(1.0-maze[i][j]));
123     }
124 }
125 
126 void dp_block(int i, int j, int cur)
127 {
128     if (!fcmp(maze[i][j]))
129         return ;
130     int k;
131     for (k = 0; k < hm[cur].size; ++k)
132     {
133         if (!fcmp(hm[cur].f[k]))
134             continue ;
135         decode(hm[cur].state[k]);
136         code[j] = 0;//同理
137         hm[cur^1].push(encode(), hm[cur].f[k]*maze[i][j]);
138     }
139 }
140 
141 double solve()
142 {
143     int i, j, cur = 0;
144     memset(code, 0, sizeof(code));
145     hm[cur].clear();
146     code[1] = 1;
147     hm[cur].push(encode(), 1);
148     for (i = 1; i <= n; ++i)
149         for (j = 1; j <= m; ++j)
150         {
151             hm[cur^1].clear();
152             dp_blank(i, j, cur);
153             dp_block(i, j, cur);
154             cur ^= 1;
155         }
156     double ret = 0;
157     for (i = 0; i < hm[cur].size; ++i)
158     {
159         decode(hm[cur].state[i]);
160         if (code[m] == 1)
161             ret += hm[cur].f[i];
162     }
163     return ret;
164 }
165 
166 void work()
167 {
168     int i, j;
169     double sum = solve();
170     for (i = 1; i <= n; ++i)
171         for (j = 1; j <= m; ++j)
172         {
173             double cache = maze[i][j];
174             maze[i][j] = 1.0;
175             printf("%.6lf%c", solve()*cache/sum, (j == m) ? '\n' : ' ');
176             maze[i][j] = cache;
177         }
178 }
179 
180 int main() 
181 {
182     int T, iCase = 0;
183     scanf("%d", &T);
184     while (T --)
185     {
186         if (iCase++)
187             printf("\n");
188         in();
189         work();
190     }
191     return 0;
192 }

 

转载于:https://www.cnblogs.com/-ZZB-/p/6440040.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值