【GDKOI 2016】地图 map 类插头DP
Description
对于一个n*m的地图,每个格子有五种可能:平地,障碍物,出口,入口和神器。一个有效的地图必须满足下列条件:
1.入口,出口和神器都有且仅出现一次,并且不在同一个格子内。
2.入口,出口和神器两两都是连通的。
连通性判断为四连通。
现在给出一个n*m的地图,其中一些格子的状态已经确定,另一些格子的状态未确定。
问当所有的格子状态确定之后,有多少种情况使得该地图是一个有效的地图?输出结构为答案模1e9+7。
Input
第一行输入两个整数n和m,意义如题目所示。接下来n行,每行m个字符:
字符'.'表示平地
字符'#'表示障碍物
字符'?'表示未确定
字符'S'表示入口
字符'X'表示神器
字符'E'表示出口
Output
一行,表示方案数
Sample Input
2 3
S#E
???
Sample Output
3
HINT
对于30%的数据,?数量小于10
对于100%的数据,1<=n<=7,1<=m<=7
Solution
这是一道类插头DP的题目,做法与插头DP类似。
对于'?',我们可以枚举情况;而对于其他已经确定了的状态,可以直接按格DP。
状态只需记录格子所在的连通块,并在最后记录入口,出口和神器所在的连通块,HASH存。
Code
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <string> 5 #include <algorithm> 6 7 using namespace std; 8 9 #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i) 10 #define DWN(i, a, b) for (int i = (a), i##_end_ = (b); i >= i##_end_; --i) 11 #define mset(a, b) memset(a, b, sizeof(a)) 12 typedef long long LL; 13 const int MAXD = 16, HASH = 30007, STATE = 100010, MOD = 1e9+7; 14 int n, m, code[MAXD], ch[MAXD], x[3], y[3]; 15 char maze[MAXD][MAXD], sp[5] = {'S', 'X', 'E', '.', '#'}; 16 17 void add(LL &x, LL y) { x += y; if (x >= MOD) x -= MOD; } 18 19 struct HASHMAP 20 { 21 int head[HASH], nxt[STATE], siz; LL state[STATE], f[STATE]; 22 void clear() { siz = 0, mset(head, -1); } 23 void push(LL x, LL k) 24 { 25 int pos = x%HASH, i = head[pos]; 26 for (; i != -1; i = nxt[i]) 27 if (state[i] == x) { add(f[i], k); return ; } 28 state[siz] = x, f[siz] = k; 29 nxt[siz] = head[pos], head[pos] = siz++; 30 } 31 }hm[2]; 32 33 void in() 34 { 35 scanf("%d %d", &n, &m); 36 mset(x, -1), mset(y, -1); 37 REP(i, 1, n) 38 { 39 scanf("%s", maze[i]+1); 40 REP(j, 1, m) 41 { 42 if (maze[i][j] == 'S') x[0] = i, y[0] = j; 43 else if (maze[i][j] == 'X') x[1] = i, y[1] = j; 44 else if (maze[i][j] == 'E') x[2] = i, y[2] = j; 45 } 46 } 47 } 48 49 bool check(int i, int j) 50 { 51 if (maze[i][j] == 'S' && code[m+1]) return 1; 52 else if (maze[i][j] == 'X' && code[m+2]) return 1; 53 else if (maze[i][j] == 'E' && code[m+3]) return 1; 54 else return 0; 55 } 56 57 void decode(LL x) 58 { 59 REP(i, 1, m+3) code[i] = x&7, x >>= 3; 60 } 61 62 LL encode(int i, int j) 63 { 64 if (maze[i][j] == 'S') code[m+1] = code[j]; 65 else if (maze[i][j] == 'X') code[m+2] = code[j]; 66 else if (maze[i][j] == 'E') code[m+3] = code[j]; 67 LL ret = 0; int cnt = 0; 68 mset(ch, -1), ch[0] = 0; 69 DWN(t, m+3, 1) 70 { 71 if (ch[code[t]] == -1) ch[code[t]] = ++cnt; 72 ret <<= 3, ret |= ch[code[t]]; 73 } 74 return ret; 75 } 76 77 void dp_blank(int i, int j, int cur) 78 { 79 REP(k, 0, hm[cur].siz-1) 80 { 81 decode(hm[cur].state[k]); 82 if (check(i, j)) continue ; 83 int lef = code[j-1], up = code[j], id = 13; 84 if (lef) id = min(id, lef); 85 if (up) id = min(id, up); 86 if (lef) 87 REP(t, 1, m+3) if (code[t] == lef) code[t] = id; 88 if (up) 89 REP(t, 1, m+3) if (code[t] == up) code[t] = id; 90 code[j] = id; 91 hm[cur^1].push(encode(i, j), hm[cur].f[k]); 92 } 93 } 94 95 void dp_block(int i, int j, int cur) 96 { 97 REP(k, 0, hm[cur].siz-1) 98 { 99 decode(hm[cur].state[k]), code[j] = 0; 100 hm[cur^1].push(encode(i, j), hm[cur].f[k]); 101 } 102 } 103 104 void work() 105 { 106 int cur = 0; LL ans = 0; 107 hm[0].clear(), hm[1].clear(), hm[0].push(0, 1); 108 REP(i, 1, n) 109 REP(j, 1, m) 110 { 111 if (maze[i][j] != '?') 112 { 113 if (maze[i][j] == '#') dp_block(i, j, cur); 114 else dp_blank(i, j, cur); 115 } 116 else 117 { 118 REP(t, 0, 4) 119 { 120 if (t < 3 && x[t] != -1) continue ; 121 maze[i][j] = sp[t]; 122 if (maze[i][j] == '#') dp_block(i, j, cur); 123 else dp_blank(i, j, cur); 124 } 125 } 126 hm[cur].clear(), cur ^= 1; 127 } 128 REP(i, 0, hm[cur].siz-1) 129 { 130 decode(hm[cur].state[i]); 131 if ((!code[m+1]) || (!code[m+2]) || (!code[m+3])) continue ; 132 int t = code[m+1]; 133 if (code[m+2] != t || code[m+3] != t) continue ; 134 add(ans, hm[cur].f[i]); 135 } 136 printf("%I64d\n", ans); 137 } 138 139 int main() 140 { 141 in(); 142 work(); 143 return 0; 144 }
Nothing is impossible!