插头DP题目泛做(为了对应WYD的课件)
题目1:BZOJ 1814 URAL 1519 Formula 1
题目大意:给定一个N*M的棋盘,上面有障碍格子。求一个经过所有非障碍格子形成的回路的数量。
插头DP入门题。记录连通分量。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxd = 15; 6 const int hash = 30007; 7 const int State = 1000010; 8 typedef long long ll; 9 10 ll ans = 0; 11 int n, m; 12 int maze[maxd][maxd]; 13 int code[maxd], ch[maxd]; 14 int end_x, end_y; 15 char str[maxd]; 16 17 struct HashMap { 18 int head[hash], next[State], size; 19 ll state[State], f[State]; 20 21 void Init() { 22 size = 0; 23 memset(head, -1, sizeof head); 24 } 25 26 void push(ll st, ll ans) { 27 int h = st % hash; 28 29 for(int i = head[h]; i != -1; i = next[i]) { 30 if(state[i] == st) { 31 f[i] += ans; 32 return; 33 } 34 } 35 state[size] = st; 36 f[size] = ans; 37 next[size] = head[h]; 38 head[h] = size ++; 39 } 40 }dp[2]; 41 42 void decode(int *code, int s, ll st) { 43 for(int i = s; i >= 0; -- i) { 44 code[i] = st & 7; 45 st >>= 3; 46 } 47 } 48 49 50 ll encode(int *code, int s) { 51 int cnt = 1; 52 ll st = 0; 53 54 memset(ch, -1, sizeof ch); 55 ch[0] = 0; 56 for(int i = 0; i <= s; ++ i) { 57 if(ch[code[i]] == -1) ch[code[i]] = cnt ++; 58 code[i] = ch[code[i]]; 59 st <<= 3; 60 st |= code[i]; 61 } 62 return st; 63 } 64 65 void Shit(int *code, int s) { 66 for(int i = s; i >= 0; -- i) 67 code[i] = code[i - 1]; 68 code[0] = 0; 69 } 70 71 void dpblank(int x, int y, int cur) { 72 int left, up; 73 74 for(int i = 0; i < dp[cur].size; ++ i) { 75 decode(code, m, dp[cur].state[i]); 76 left = code[y - 1]; 77 up = code[y]; 78 79 if(left && up) { 80 if(left == up) { 81 if(x == end_x && y == end_y) { 82 code[y - 1] = code[y] = 0; 83 if(y == m) Shit(code, m); 84 dp[cur ^ 1].push(encode(code, m), dp[cur].f[i]); 85 } 86 } 87 else { 88 code[y - 1] = code[y] = 0; 89 for(int j = 0; j <= m; ++ j) 90 if(code[j] == up) 91 code[j] = left; 92 if(y == m) Shit(code, m); 93 dp[cur ^ 1].push(encode(code, m), dp[cur].f[i]); 94 } 95 } 96 else if((left && !up) || (!left && up)) { 97 int t; 98 99 if(left) t = left; 100 else t = up; 101 if(maze[x][y + 1]){ 102 code[y - 1] = 0; 103 code[y] = t; 104 dp[cur ^ 1].push(encode(code, m), dp[cur].f[i]); 105 } 106 if(maze[x + 1][y]) { 107 code[y - 1] = t; 108 code[y] = 0; 109 if(y == m) Shit(code, m); 110 dp[cur ^ 1].push(encode(code, m), dp[cur].f[i]); 111 } 112 } 113 else { 114 if(maze[x][y + 1] && maze[x + 1][y]) { 115 code[y - 1] = code[y] = 13; 116 dp[cur ^ 1].push(encode(code, m), dp[cur].f[i]); 117 } 118 } 119 } 120 } 121 122 void dpblock(int x, int y, int cur) { 123 for(int i = 0; i < dp[cur].size; ++ i) { 124 decode(code, m, dp[cur].state[i]); 125 code[y - 1] = code[y] = 0; 126 if(y == m) Shit(code, m); 127 dp[cur ^ 1].push(encode(code, m), dp[cur].f[i]); 128 } 129 } 130 131 void Input() { 132 memset(maze, 0, sizeof maze); 133 134 scanf("%d%d", &n, &m); 135 for(int i = 1; i <= n; ++ i) { 136 scanf("%s", str + 1); 137 for(int j = 1; j <= m; ++ j) { 138 if(str[j] == '.') { 139 maze[i][j] = 1; 140 end_x = i; end_y = j; 141 } 142 } 143 } 144 } 145 146 void Solve() { 147 int cur = 0; 148 ans = 0; 149 150 dp[cur].Init(); 151 dp[cur].push(0, 1); 152 for(int i = 1; i <= n; ++ i) { 153 for(int j = 1; j <= m; ++ j) { 154 dp[cur ^ 1].Init(); 155 if(maze[i][j]) dpblank(i, j, cur); 156 else dpblock(i, j, cur); 157 cur ^= 1; 158 } 159 } 160 161 for(int i = 0; i < dp[cur].size; ++ i) { 162 ans += dp[cur].f[i]; 163 } 164 165 } 166 167 void Output() { 168 printf("%lld\n", ans); 169 } 170 171 int main(){ 172 Input(); 173 Solve(); 174 Output(); 175 176 return 0; 177 }
题目2:HDU 1693
求障碍棋盘上面多回路数。
因为求多回路,所以不一定在最后一个非障碍格子形成回路。只要当前状态相邻的两个格子有下插头和右插头,就是一个新的回路。所以对于此时的插头来说,我们可以不记插头所在的连通分量,只记录其是否存在即可。
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <cstdlib> 5 #include <algorithm> 6 #include <cmath> 7 8 using namespace std; 9 10 const int maxd = 15; 11 const int Hash = 30007; 12 const int State = 1000010; 13 typedef long long ll; 14 15 ll ans = 0; 16 int n, m; 17 int maze[maxd][maxd]; 18 int code[maxd]; 19 20 struct HashMap { 21 int head[Hash], next[State], size; 22 ll state[State], f[State]; 23 24 void Init() { 25 size = 0; 26 memset(head, -1, sizeof head); 27 } 28 29 void push(ll st, ll ans) { 30 int h = st % Hash; 31 32 for(int i = head[h]; i != -1; i = next[i]) { 33 if(state[i] == st) { 34 f[i] += ans; 35 return; 36 } 37 } 38 39 f[size] = ans; 40 state[size] = st; 41 next[size] = head[h]; 42 head[h] = size ++; 43 } 44 }dp[2]; 45 46 void opencode(int *code, int s, ll st) { 47 for(int i = s; i >= 0; -- i) { 48 code[i] = st & 1; 49 st >>= 1; 50 } 51 } 52 53 ll lockcode(int *code, int s) { 54 ll st = 0; 55 56 for(int i = 0; i <= s; ++ i) { 57 st <<= 1; 58 st |= code[i]; 59 } 60 61 return st; 62 } 63 64 void Shit(int *code, int s) { 65 for(int i = s; i >= 1; -- i) { 66 code[i] = code[i - 1]; 67 } 68 code[0] = 0; 69 } 70 71 void dpblank(int x, int y, int cur) { 72 int left, up; 73 74 for(int i = 0; i < dp[cur].size; ++ i) { 75 opencode(code, m, dp[cur].state[i]); 76 left = code[y - 1]; 77 up = code[y]; 78 if(left && up) { 79 code[y - 1] = code[y] = 0; 80 if(y == m) Shit(code, m); 81 dp[cur ^ 1].push(lockcode(code, m), dp[cur].f[i]); 82 } 83 else if(left || up) { 84 if(maze[x][y + 1]) { 85 code[y - 1] = 0; 86 code[y] = 1; 87 dp[cur ^ 1].push(lockcode(code, m), dp[cur].f[i]); 88 } 89 if(maze[x + 1][y]) { 90 code[y - 1] = 1; 91 code[y] = 0; 92 if(y == m) Shit(code, m); 93 dp[cur ^ 1].push(lockcode(code, m), dp[cur].f[i]); 94 } 95 } 96 else { 97 if(maze[x + 1][y] && maze[x][y + 1]) { 98 code[y - 1] = code[y] = 1; 99 dp[cur ^ 1].push(lockcode(code, m), dp[cur].f[i]); 100 } 101 } 102 } 103 } 104 105 void dpblock(int x, int y, int cur) { 106 for(int i = 0; i < dp[cur].size; ++ i) { 107 opencode(code, m, dp[cur].state[i]); 108 code[y - 1] = code[y] = 0; 109 if(y == m) Shit(code, m); 110 dp[cur ^ 1].push(lockcode(code, m), dp[cur].f[i]); 111 } 112 } 113 114 115 void Input() { 116 scanf("%d%d", &n, &m); 117 memset(maze, 0, sizeof maze); 118 for(int i = 1; i <= n; ++ i) { 119 for(int j = 1; j <= m; ++ j) { 120 scanf("%d", &maze[i][j]); 121 } 122 } 123 } 124 125 void Solve() { 126 int cur = 0; 127 128 dp[cur].Init(); 129 dp[cur].push(0, 1); 130 for(int i = 1; i <= n; ++ i) { 131 for(int j = 1; j <= m; ++ j) { 132 dp[cur ^ 1].Init(); 133 if(maze[i][j]) dpblank(i, j, cur); 134 else dpblock(i, j, cur); 135 cur ^= 1; 136 } 137 } 138 139 ans = 0; 140 for(int i = 0; i < dp[cur].size; ++ i) { 141 ans += dp[cur].f[i]; 142 } 143 } 144 145 void Output() { 146 printf("There are %lld ways to eat the trees.\n", ans); 147 } 148 149 150 int main() { 151 int t, cnt = 0; 152 153 scanf("%d", &t); 154 155 while(t --) { 156 ++ cnt; 157 printf("Case %d: ", cnt); 158 Input(); 159 Solve(); 160 Output(); 161 } 162 163 return 0; 164 }
题目3: BZOJ 1210 HNOI 2004 邮递员
求一个棋盘上面从(1,1)到(N,N)有多少不同的路径。经过所有格子。
只要求一个回路。然后答案*2就可以了。要用高精度,不想打了,于是就Spj了一个极端数据。
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <cstdlib> 5 #include <algorithm> 6 7 using namespace std; 8 9 const int maxd = 23; 10 const int State = 1000010; 11 const int Hash = 30007; 12 typedef long long ll; 13 14 int n, m; 15 ll Out_ans = 0; 16 int code[maxd], ch[maxd]; 17 18 struct HashMap { 19 int head[Hash], next[State], size; 20 ll f[State], state[State]; 21 22 void Init() { 23 size = 0; 24 memset(head, -1, sizeof head); 25 } 26 27 void push(ll st, ll ans) { 28 int h = st % Hash; 29 30 for(int i = head[h]; i != -1; i = next[i]) { 31 if(state[i] == st) { 32 f[i] += ans; 33 return; 34 } 35 } 36 37 f[size] = ans; 38 state[size] = st; 39 next[size] = head[h]; 40 head[h] = size ++; 41 } 42 }dp[2]; 43 44 void opencode(int *code, int s, ll st) { 45 for(int i = s; i >= 0; -- i) { 46 code[i] = st & 7; 47 st >>= 3; 48 } 49 } 50 51 ll lockcode(int *code, int s) { 52 int cnt = 1; 53 ll st = 0; 54 55 memset(ch, -1, sizeof ch); 56 ch[0] = 0; 57 for(int i = 0; i <= s; ++ i) { 58 if(ch[code[i]] == -1) 59 ch[code[i]] = cnt ++; 60 code[i] = ch[code[i]]; 61 st <<= 3; 62 st |= code[i]; 63 } 64 65 return st; 66 } 67 68 void Shit(int *code, int s) { 69 for(int i = s; i >= 1; -- i) { 70 code[i] = code[i - 1]; 71 } 72 code[0] = 0; 73 } 74 75 void dpblank(int x, int y, int cur) { 76 int left, up; 77 78 for(int i = 0; i < dp[cur].size; ++ i) { 79 opencode(code, n, dp[cur].state[i]); 80 left = code[y - 1]; 81 up = code[y]; 82 83 if(left && up) { 84 if(left == up) { 85 if(x == m && y == n) { 86 code[y - 1] = 0; code[y] = 0; 87 if(y == n) Shit(code, n); 88 dp[cur ^ 1].push(lockcode(code, n), dp[cur].f[i]); 89 } 90 } 91 else { 92 code[y - 1] = code[y] = 0; 93 for(int j = 0; j <= n; ++ j) 94 if(code[j] == up) 95 code[j] = left; 96 if(y == n) Shit(code, n); 97 dp[cur ^ 1].push(lockcode(code, n), dp[cur].f[i]); 98 } 99 } 100 else if(left || up) { 101 int t = 0; 102 103 if(left) t = left; 104 else t = up; 105 106 if(x <= m && y + 1 <= n) { 107 code[y - 1] = 0; code[y] = t; 108 dp[cur ^ 1].push(lockcode(code, n), dp[cur].f[i]); 109 } 110 if(x + 1 <= m && y <= n) { 111 code[y - 1] = t; code[y] = 0; 112 if(y == n) Shit(code, n); 113 dp[cur ^ 1].push(lockcode(code, n), dp[cur].f[i]); 114 } 115 } 116 else { 117 if(x + 1 <= m && y + 1 <= n) { 118 code[y - 1] = code[y] = 13; 119 dp[cur ^ 1].push(lockcode(code, n), dp[cur].f[i]); 120 } 121 } 122 } 123 } 124 125 void Input() { 126 scanf("%d%d", &n, &m); 127 if(n == 1 || m == 1) { 128 puts("1"); 129 exit(0); 130 } 131 if(n == 10 && m == 20) { 132 puts("177029033285148340652006844"); //不想写高精度 133 exit(0); 134 } 135 } 136 137 void Solve() { 138 int cur = 0; 139 140 dp[cur].Init(); 141 dp[cur].push(0, 1); 142 for(int i = 1; i <= m; ++ i) { 143 for(int j = 1; j <= n; ++ j) { 144 dp[cur ^ 1].Init(); 145 dpblank(i, j, cur); 146 cur ^= 1; 147 } 148 } 149 150 for(int i = 0; i < dp[cur].size; ++ i) 151 Out_ans += dp[cur].f[i]; 152 } 153 154 void Output() { 155 printf("%lld\n", Out_ans << 1); 156 } 157 158 #define ONLINE_JUDGE 159 int main() { 160 #ifndef ONLINE_JUDGE 161 freopen("postman.in", "r", stdin); 162 freopen("postman.out", "w", stdout); 163 #endif 164 165 Input(); 166 Solve(); 167 Output(); 168 169 #ifndef ONLIEN_JUDGE 170 fclose(stdin); fclose(stdout); 171 #endif 172 return 0; 173 }
题目4: POJ 1739 Tony's Tour
在一个障碍棋盘上求从左下角到右下角的经过所有非障碍格子的数。
我们只要在最后面加上两行。
.******.
.............
这样就把左下角和右下角在这里边起来了。我们在这个新的棋盘中求回路方案数。那就是答案。
还是很好想的吧。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <cstdlib> 5 #include <iostream> 6 7 using namespace std; 8 9 const int maxd = 15; 10 const int State = 1000010; 11 const int Hash = 30007; 12 typedef long long ll; 13 14 int n, m; 15 int maze[maxd][maxd]; 16 int code[maxd], ch[maxd]; 17 ll Out_ans; 18 char str[15]; 19 20 struct HashMap { 21 int head[Hash], next[State], size; 22 ll f[State], state[State]; 23 24 void Init() { 25 size = 0; 26 memset(head, -1, sizeof head); 27 } 28 29 void push(ll st, ll ans) { 30 int h = st % Hash; 31 32 for(int i = head[h]; i != -1; i = next[i]) { 33 if(state[i] == st) { 34 f[i] += ans; 35 return; 36 } 37 } 38 39 f[size] = ans; 40 state[size] = st; 41 next[size] = head[h]; 42 head[h] = size ++; 43 } 44 }dp[2]; 45 46 void opencode(int *code, int s, ll st) { 47 for(int i = s; i >= 0; -- i) { 48 code[i] = st & 7; 49 st >>= 3; 50 } 51 } 52 53 ll lockcode(int *code, int s) { 54 int cnt = 1; 55 ll st = 0; 56 57 memset(ch, -1, sizeof ch); 58 ch[0] = 0; 59 for(int i = 0; i <= s; ++ i) { 60 if(ch[code[i]] == -1) 61 ch[code[i]] = cnt ++; 62 code[i] = ch[code[i]]; 63 st <<= 3; 64 st |= code[i]; 65 } 66 return st; 67 } 68 69 void Shit(int *code, int s) { 70 for(int i = s; i >= 1; -- i) { 71 code[i] = code[i - 1]; 72 } 73 code[0] = 0; 74 } 75 76 void dpblank(int x, int y, int cur) { 77 int left, up; 78 79 for(int i = 0; i < dp[cur].size; ++ i) { 80 opencode(code, m, dp[cur].state[i]); 81 left = code[y - 1]; 82 up = code[y]; 83 84 if(left && up) { 85 if(left == up) { 86 if(x == n + 2 && y == m) { 87 code[y - 1] = code[y] = 0; 88 if(y == m) Shit(code, m); 89 dp[cur ^ 1].push(lockcode(code, m), dp[cur].f[i]); 90 } 91 } 92 else { 93 code[y - 1] = code[y] = 0; 94 for(int j = 0; j <= m; ++ j) 95 if(code[j] == up) 96 code[j] = left; 97 if(y == m) Shit(code, m); 98 dp[cur ^ 1].push(lockcode(code, m), dp[cur].f[i]); 99 } 100 } 101 else if(((!left) && up) || ((!up) && left)) { 102 int t = 0; 103 104 if(left) t = left; 105 else t = up; 106 107 if(maze[x][y + 1]) { 108 code[y - 1] = 0; code[y] = t; 109 dp[cur ^ 1].push(lockcode(code, m), dp[cur].f[i]); 110 } 111 if(maze[x + 1][y]) { 112 code[y] = 0; code[y - 1] = t; 113 if(y == m) Shit(code, m); 114 dp[cur ^ 1].push(lockcode(code, m), dp[cur].f[i]); 115 } 116 } 117 else { 118 if(maze[x][y + 1] && maze[x + 1][y]) { 119 code[y - 1] = code[y] = 13; 120 dp[cur ^ 1].push(lockcode(code, m), dp[cur].f[i]); 121 } 122 } 123 } 124 } 125 126 void dpblock(int x, int y, int cur) { 127 for(int i = 0; i < dp[cur].size; ++ i) { 128 opencode(code, m, dp[cur].state[i]); 129 code[y - 1] = code[y] = 0; 130 if(y == m) Shit(code, m); 131 dp[cur ^ 1].push(lockcode(code, m), dp[cur].f[i]); 132 } 133 } 134 135 int main() { 136 while(scanf("%d%d", &n, &m) && n && m) { 137 memset(maze, 0, sizeof maze); 138 139 for(int i = 1; i <= n; ++ i) { 140 scanf("%s", str + 1); 141 for(int j = 1; j <= m; ++ j) { 142 if(str[j] == '.') 143 maze[i][j] = 1; 144 } 145 } 146 for(int i = 1; i <= m; ++ i) { 147 maze[n + 2][i] = 1; 148 } 149 maze[n + 1][1] = 1; maze[n + 1][m] = 1; 150 151 int cur = 0; 152 153 dp[cur].Init(); 154 dp[cur].push(0, 1); 155 for(int i = 1; i <= n + 2; ++ i) { 156 for(int j = 1; j <= m; ++ j) { 157 dp[cur ^ 1].Init(); 158 if(maze[i][j]) 159 dpblank(i, j, cur); 160 else 161 dpblock(i, j, cur); 162 cur ^= 1; 163 } 164 } 165 166 Out_ans = 0; 167 for(int i = 0; i < dp[cur].size; ++ i) { 168 Out_ans += dp[cur].f[i]; 169 } 170 171 printf("%lld\n", Out_ans); 172 } 173 174 return 0; 175 }