题意:
有一个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) ? ' ' : ' '); 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(" "); 188 in(); 189 work(); 190 } 191 return 0; 192 }