插头DP
一般是求网格图路径个数/最值的。
维护轮廓线连通性。按照格子转移。
注意跨行时的转移。
例题:bzoj1814 注意!结尾不一定是(n, m),此时要保证没有插头才能加入答案。
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <cmath> 5 6 typedef long long LL; 7 const int N = 14, M = 1600000; 8 9 LL f[2][M]; 10 int n, m, G[N][N], now[N], state[M], id[M], top; 11 char str[N]; 12 13 inline int zip(int *a) { 14 int ans = 0; 15 for(int i = m; i >= 0; i--) { 16 ans = ans * 3 + a[i]; 17 } 18 return id[ans]; 19 } 20 21 inline void DFS(int k, int cnt, int sta) { 22 if(k > m) { 23 if(cnt == 0) { 24 state[++top] = sta; 25 id[sta] = top; 26 //printf("state ++top = %d \n", state[top]); 27 } 28 return; 29 } 30 DFS(k + 1, cnt, sta * 3); 31 if(cnt) { 32 DFS(k + 1, cnt - 1, sta * 3 + 1); 33 } 34 DFS(k + 1, cnt + 1, sta * 3 + 2); 35 return; 36 } 37 38 inline void prework() { 39 DFS(0, 0, 0); 40 return; 41 } 42 43 inline void unzip(int x, int *a) { // m + 1 44 x = state[x]; 45 for(int i = 0; i <= m; i++) { 46 a[i] = x % 3; 47 x /= 3; 48 } 49 return; 50 } 51 52 inline void add(LL &a, LL b) { 53 a += b; 54 return; 55 } 56 57 inline void out() { 58 for(int i = 0; i <= m; i++) { 59 printf("%d", now[i]); 60 } 61 return; 62 } 63 64 inline int get2(int p) { 65 int cnt = 0; 66 while(1) { 67 //printf("get 2 \n"); 68 if(now[p] == 2 && !cnt) { 69 return p; 70 } 71 if(now[p] == 1) { 72 cnt++; 73 } 74 else if(now[p] == 2) { 75 cnt--; 76 } 77 p++; 78 } 79 } 80 81 inline int get1(int p) { 82 int cnt = 0; 83 while(1) { 84 //printf("get 1 \n"); 85 if(now[p] == 1 && !cnt) { 86 return p; 87 } 88 if(now[p] == 2) { 89 cnt++; 90 } 91 else if(now[p] == 1) { 92 cnt--; 93 } 94 p--; 95 } 96 } 97 98 inline char gc() { 99 char c = getchar(); 100 while(c != '*' && c != '.') c = getchar(); 101 return c; 102 } 103 104 int main() { 105 106 //printf("%d", sizeof(f) / 1048576); 107 //freopen("in.in", "r", stdin); 108 //freopen("my.out", "w", stdout); 109 110 scanf("%d%d", &n, &m); 111 for(int i = 0; i < n; i++) { 112 for(int j = 0; j < m; j++) { 113 G[i][j] = (gc() == '*'); 114 } 115 } 116 117 int last_x, last_y; 118 for(int i = n - 1; i >= 0; i--) { 119 for(int j = m - 1; j >= 0; j--) { 120 if(!G[i][j]) { 121 last_x = i; 122 last_y = j; 123 i = -1; 124 break; 125 } 126 } 127 } 128 129 prework(); 130 131 int lm = pow(3, m + 1), flag = 1; 132 LL ans = 0; 133 f[0][id[0]] = 1; 134 for(int i = 0; i < n; i++) { 135 for(int j = 0; j < m; j++) { 136 flag ^= 1; 137 for(int s = 1; s <= top; s++) { 138 f[flag ^ 1][s] = 0; 139 } 140 //printf("%d %d \n", i, j); 141 for(int s = 1; s <= top; s++) { 142 if(!f[flag][s]) { 143 continue; 144 } 145 unzip(s, now); 146 // f[i][j][s] -> f[i][j + 1][?] 147 /// now[j] now[j + 1] 148 // DP 149 LL c = f[flag][s]; 150 //printf(" > > s :"); out(); printf(" %d \n", c); 151 if(G[i][j]) { 152 if(!now[j] && !now[j + 1]) { 153 add(f[flag ^ 1][s], c); 154 } 155 continue; 156 } 157 if(!now[j] && !now[j + 1]) { /// 0 0 158 now[j] = 1; 159 now[j + 1] = 2; 160 add(f[flag ^ 1][zip(now)], c); 161 now[j] = now[j + 1] = 0; 162 } 163 else if(!now[j] || !now[j + 1]) { /// [0 1/2] [1/2 0] hold / swap 164 add(f[flag ^ 1][s], c); 165 std::swap(now[j], now[j + 1]); 166 add(f[flag ^ 1][zip(now)], c); 167 std::swap(now[j], now[j + 1]); 168 } 169 else if(now[j] == 1 && now[j + 1] == 1) { /// 1 1 the first 2 -> 1 170 int p = get2(j + 2); 171 now[p] = 1; now[j] = now[j + 1] = 0; 172 add(f[flag ^ 1][zip(now)], c); 173 now[p] = 2; now[j] = now[j + 1] = 1; 174 } 175 else if(now[j] == 2 && now[j + 1] == 2) { /// 2 2 the first 1 -> 2 176 int p = get1(j - 1); 177 now[p] = 2; now[j] = now[j + 1] = 0; 178 add(f[flag ^ 1][zip(now)], c); 179 now[1] = 1; now[j] = now[j + 1] = 2; 180 } 181 else if(now[j] == 2 && now[j + 1] == 1) { /// 2 1 merge 182 now[j] = now[j + 1] = 0; 183 add(f[flag ^ 1][zip(now)], c); 184 now[j] = 2; now[j + 1] = 1; 185 } 186 else if(i == last_x && j == last_y) { /// 1 2 END 187 bool t = 0; 188 for(int q = 0; q < j; q++) { 189 if(now[q]) { 190 t = 1; 191 break; 192 } 193 } 194 for(int q = j + 2; q <= m; q++) { 195 if(now[q]) { 196 t = 1; 197 break; 198 } 199 } 200 if(!t) add(ans, c); 201 } 202 } 203 } 204 if(i < n - 1) { 205 // change row 206 for(int s = top; s >= 1; s--) { 207 if(state[s] % 3) { 208 f[flag ^ 1][s] = 0; 209 } 210 else { 211 f[flag ^ 1][s] = f[flag ^ 1][id[state[s] / 3]]; 212 } 213 } 214 } 215 } 216 217 printf("%lld\n", ans); 218 return 0; 219 }
例题:hdu1693 不用记录左/右插头,直接DP,随意转移。
1 #include <cstdio> 2 #include <cstring> 3 4 typedef long long LL; 5 const int N = 13; 6 7 int G[N][N], m, n; 8 LL f[2][1480010]; 9 10 inline void add(LL &a, LL b) { 11 a += b; 12 return; 13 } 14 15 inline void out(int x) { 16 printf("%d ", x); 17 for(int i = 0; i <= m; i++) { 18 printf("%d", (x >> i) & 1); 19 } 20 return; 21 } 22 23 /* 24 2 25 2 4 26 1 1 1 1 27 1 1 1 1 28 */ 29 30 inline LL solve() { 31 memset(f, 0, sizeof(f)); 32 scanf("%d%d", &n, &m); 33 int last_x, last_y; 34 for(int i = 0; i < n; i++) { 35 for(int j = 0; j < m; j++) { 36 scanf("%d", &G[i][j]); 37 if(G[i][j]) { 38 last_x = i; 39 last_y = j; 40 } 41 G[i][j] ^= 1; 42 } 43 } 44 45 int lm = 1 << (m + 1), flag = 1; 46 LL ans = 0; 47 f[0][0] = 1; 48 for(int i = 0; i < n; i++) { 49 for(int j = 0; j < m; j++) { 50 flag ^= 1; 51 //printf("%d %d \n", i, j); 52 for(int s = 0; s < lm; s++) { 53 f[flag ^ 1][s] = 0; 54 } 55 for(int s = 0; s < lm; s++) { 56 //printf("f %d %d = %d \n", flag, s, f[flag][s]); 57 if(!f[flag][s]) { 58 continue; 59 } 60 int a = (s >> j) & 1, b = (s >> (j + 1)) & 1; 61 LL c = f[flag][s]; 62 //printf(" > s : "); out(s); printf(" = %d \n", c); 63 if(G[i][j]) { 64 if(!a && !b) { 65 add(f[flag ^ 1][s], c); 66 } 67 } 68 else if(!a && !b) { /// 0 0 69 add(f[flag ^ 1][s | (1 << j) | (1 << (j + 1))], c); 70 //printf(" ---> %d %d \n", flag ^ 1, s | (1 << j) | (1 << (j + 1))); 71 } 72 else if(a && b) { 73 add(f[flag ^ 1][s & (~((1 << j) | (1 << (j + 1))))], c); 74 } 75 else if(a) { 76 add(f[flag ^ 1][s], c); 77 add(f[flag ^ 1][(s | (1 << (j + 1))) & (~(1 << j))], c); 78 } 79 else if(b) { 80 add(f[flag ^ 1][s], c); 81 add(f[flag ^ 1][(s | (1 << j)) & (~(1 << (j + 1)))], c); 82 } 83 } 84 if(i == last_x && j == last_y) { 85 return f[flag ^ 1][0]; 86 } 87 } 88 /// line i -> i + 1 89 for(int s = lm - 1; s >= 0; s--) { 90 if(s & 1) { 91 f[flag ^ 1][s] = 0; 92 } 93 else { 94 f[flag ^ 1][s] = f[flag ^ 1][s >> 1]; 95 } 96 } 97 } 98 return -1; 99 } 100 101 int main() { 102 int T; 103 scanf("%d", &T); 104 for(int i = 1; i <= T; i++) { 105 LL ans = solve(); 106 printf("Case %d: There are %lld ways to eat the trees.\n", i, ans); 107 //printf("%lld \n", ans); 108 } 109 return 0; 110 }
例题:poj3133 求把网格图上2和3连通起来的最小格子数。
求最值,一样的套路...
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <algorithm> 5 6 const int N = 15, INF = 0x3f3f3f3f; 7 8 int G[N][N], now[N], m, n; 9 int f[2][60010]; 10 11 inline int zip(int *a) { 12 int ans = 0; 13 for(int i = m; i >= 0; i--) { 14 ans = ans * 3 + a[i]; 15 } 16 return ans; 17 } 18 19 inline void unzip(int x, int *a) { 20 for(int i = 0; i <= m; i++) { 21 a[i] = x % 3; 22 x /= 3; 23 } 24 return; 25 } 26 27 inline void out() { 28 for(int i = 0; i <= m; i++) { 29 printf("%d", now[i]); 30 } 31 return; 32 } 33 34 inline void exmin(int &x, int y) { 35 x > y ? x = y : 0; 36 return; 37 } 38 39 inline void solve() { 40 memset(f, 0x3f, sizeof(f)); 41 int last_x, last_y; 42 for(int i = 0; i < n; i++) { 43 for(int j = 0; j < m; j++) { 44 scanf("%d", &G[i][j]); 45 if(G[i][j] != 1) { 46 last_x = i; 47 last_y = j; 48 } 49 if(G[i][j] == 1 || G[i][j] == 3) { 50 G[i][j] = 4 - G[i][j]; 51 } 52 } 53 } 54 55 int flag = 1, lm = pow(3, m + 1); 56 f[0][0] = 0; 57 for(int i = 0; i < n; i++) { 58 for(int j = 0; j < m; j++) { 59 flag ^= 1; 60 for(int s = 0; s < lm; s++) { 61 f[flag ^ 1][s] = INF; 62 } 63 //printf("%d %d \n", i, j); 64 for(int s = 0; s < lm; s++) { 65 if(f[flag][s] == INF) { 66 continue; 67 } 68 unzip(s, now); 69 int c = f[flag][s], &a = now[j], &b = now[j + 1]; 70 71 //printf(" "); out(); printf(" = %d \n", c); 72 73 if(G[i][j] == 3) { 74 if(!a && !b) { 75 exmin(f[flag ^ 1][s], c); 76 } 77 } 78 else if(G[i][j] == 1) { 79 if(a == 1 && !b) { 80 a = 0; 81 exmin(f[flag ^ 1][zip(now)], c + 1); 82 a = 1; 83 } 84 else if(b == 1 && !a) { 85 b = 0; 86 exmin(f[flag ^ 1][zip(now)], c + 1); 87 b = 1; 88 } 89 else if(!a && !b) { 90 a = 1; 91 exmin(f[flag ^ 1][zip(now)], c + 1); 92 a = 0; 93 b = 1; 94 exmin(f[flag ^ 1][zip(now)], c + 1); 95 b = 0; 96 } 97 } 98 else if(G[i][j] == 2) { 99 if(a == 2 && !b) { 100 a = 0; 101 exmin(f[flag ^ 1][zip(now)], c + 1); 102 a = 2; 103 } 104 else if(b == 2 && !a) { 105 b = 0; 106 exmin(f[flag ^ 1][zip(now)], c + 1); 107 b = 2; 108 } 109 else if(!a && !b) { 110 a = 2; 111 exmin(f[flag ^ 1][zip(now)], c + 1); 112 a = 0; 113 b = 2; 114 exmin(f[flag ^ 1][zip(now)], c + 1); 115 b = 0; 116 } 117 } /// G[i][j] == 0 118 else if(!a && !b) { /// 0 0 119 exmin(f[flag ^ 1][s], c); // not choose 120 a = b = 1; 121 exmin(f[flag ^ 1][zip(now)], c + 1); // choose 1 122 a = b = 2; 123 exmin(f[flag ^ 1][zip(now)], c + 1); // choose 2 124 a = b = 0; 125 } 126 else if(b == 1 && a == 1) { /// 1 1 merge 127 a = b = 0; 128 exmin(f[flag ^ 1][zip(now)], c + 1); 129 a = b = 1; 130 } 131 else if(a == 2 && b == 2) { /// 2 2 merge 132 a = b = 0; 133 exmin(f[flag ^ 1][zip(now)], c + 1); 134 a = b = 2; 135 } 136 else if(a == 0 || b == 0) { /// a=0 / b=0 swap 137 exmin(f[flag ^ 1][s], c + 1); 138 std::swap(a, b); 139 exmin(f[flag ^ 1][zip(now)], c + 1); 140 std::swap(a, b); 141 } 142 } 143 if(i == last_x && j == last_y) { 144 if(f[flag ^ 1][0] == INF) puts("0"); 145 else printf("%d\n", f[flag ^ 1][0] - 2); 146 return; 147 } 148 } 149 for(int s = lm - 1; s >= 0; s--) { 150 if(s % 3) { 151 f[flag ^ 1][s] = INF; 152 } 153 else { 154 f[flag ^ 1][s] = f[flag ^ 1][s / 3]; 155 } 156 } 157 } 158 159 return; 160 } 161 162 int main() { 163 scanf("%d%d", &n, &m); 164 while(n) { 165 solve(); 166 scanf("%d%d", &n, &m); 167 } 168 return 0; 169 }