华容道——持续改进中
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef unsigned uint; const char* NM[][4] = { "曹","贼","真","爽", {"西","施"}, {"昭","君"}, {"貂","蝉"}, {"甄","姬"}, {"玉","环"}, {"美"}, {"美"}, {"美"}, {"美"} }; int W[] = { 2, 2, 2, 2, 2, 2, 1, 1, 1, 1 }; // 默认5个水平条,随后修改 int H[] = { 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; const int D[][2] = { 0, -1, 0, 1, -1, 0, 1, 0 }; enum { QMAX = 38 * 1000 * 1000 }; struct State { unsigned xs, ys; int p; // 路径的previous void operator=(const char* s); void print(const char* s = ""); } states[QMAX + 10 * 4]; int qh, qt = 1; // queue head, tail int a[10][2], b[5][4]; // 坐标与棋盘;int比byte稍快 unsigned seen[1 << (30 - 5)]; // 30位y;32=1<<5 // 宏的名字和(间不能有空格 #define froma(s) for (int i = 0; i < 10; i++) { (s).xs = ((s).xs << 2) | a[i][0]; (s).ys = ((s).ys << 3) | a[i][1]; } // 修改全局变量b;调函数开销还是挺大; \后不能有注释 #define toary(s) { \ memset(b, 1, sizeof(b)); \ uint X = (s).xs, Y = (s).ys; \ for (int i = 9; i >= 0; i--) { \ const int& x = a[i][0] = X & 3; X >>= 2; \ const int& y = a[i][1] = Y & 7; Y >>= 3; \ for (int yy = y; yy < y + H[i]; yy++) \ for (int xx = x; xx < x + W[i]; xx++) \ b[yy][xx] = 0; \ } \ } void State::operator= (const char* s) { int p = 6; for (int x = 3; x >= 0; x--) for (int y = 4; y >= 0; y--) { #define CASE(c, i) case c: a[i][0] = x; a[i][1] = y; break; switch (s[x * 5 + y]) { CASE('c', 0) CASE('g', 1) // 曹关 CASE('z', 2) CASE('h', 3) // 张黄 CASE('l', 4) CASE('m', 5) // 子龙(l) 马 case 'p': a[p][0] = x; a[p++][1] = y; // pawn } } static const char* S[] = { "gg", "zz", "hh", "ll", "mm" }; for (int i = 0; i < 5; i++) if (strstr(s, S[i])) W[i+1] = 1, H[i+1] = 2; froma(*this); } void State::print (const char* s) { toary(*this); const char* b[5][4] = {}; int idx[10] = {}; for (int i = 0; i < 10; i++) { int x = a[i][0], y = a[i][1]; for (int yy = y; yy < y + H[i]; yy++) for (int xx = x; xx < x + W[i]; xx++) b[yy][xx] = NM[i][idx[i]++]; } for (int y = 0; y < 5; y++) { for (int x = 0; x < 4; x++) printf("%s", b[y][x] ? : " "); puts(""); } printf("%s\n", s); } void print_path () { // 用数组存放的单链表就地翻转 int prev = -1, next = -1, n = 0; for (int cur = qh; cur != -1;) { next = states[cur].p; states[cur].p = prev; prev = cur; cur = next; ++n; } for (int p = 0; p != -1; p = states[p].p) states[p].print(); printf("%d\n", n); } #define peek(i) const uint key = states[i].xs ^ states[i].ys, ki = key >> 5, km = 1 << (key & 0x1f) #define aboo seen[ki] |= km int main (int argc, char* argv[]) { //states[0] = "pp zz""ccghh""ccgll""pp mm"; states[0] = (argc == 2) ? argv[1] : "pzzpg""cc g""ccllm""phhpm"; states[0].p = -1; peek(0); aboo; for (; qh < qt; qh++) { toary(states[qh]); if (a[0][1] == 3) { print_path(); break; } // 曹操的y for (int i = 9; i >= 0; i--) for (int j = 0; j < 4; j++) { int ok; const int ox = a[i][0], oy = a[i][1]; int x = (a[i][0] += D[j][0]), y = (a[i][1] += D[j][1]); if (x < 0 || x + W[i] > 4 || y < 0 || y + H[i] > 5) ok = 0; else { if (i >= 6) { // 卒单独处理;没啥用 if (j == 1) y = oy + H[i]; else if (j == 3) x = ox + W[i]; ok = b[y][x]; } else { if (j == 0) ok = b[y][x] & (!(W[i] - 1) | b[y][x + 1]); // W[i]1或2; 不更快 else if (j == 1) { y = oy + H[i]; ok = b[y][x] && (W[i] == 1 || b[y][x + 1]); } else if (j == 2) ok = b[y][x] & (!(H[i] - 1) | b[y + 1][x]); else { x = ox + W[i]; ok = b[y][x] && (H[i] == 1 || b[y + 1][x]); } } } if (ok) { froma(states[qt]); peek(qt); if (seen[ki] & km) states[qt].xs = states[qt].ys = 0; else { aboo; states[qt++].p = qh; } } a[i][0] -= D[j][0]; a[i][1] -= D[j][1]; } } printf("%d\n", qt); return 0; }
x在0~3之间,2位,y在0~4之间,3位。10个棋子,所有的x用20位,y用30位,拼起来50位。
用64位int的话,可以找到最优解。此版本找到的是不很优的:有些局面被误判为已见过,绕远了。
〔这里〕:
- 67行Python DFS
- C++ BFS 最优解。64位别忘了常数加ull后缀,变量uint64_t(x)。
0.01458秒版(-m avx2...):
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> typedef unsigned uint; const char* NM[][4] = { "曹","贼","真","爽", {"西","施"}, {"昭","君"}, {"貂","蝉"}, {"甄","姬"}, {"玉","环"}, {"美"}, {"美"}, {"美"}, {"美"} }; int W[] = { 2, 2, 2, 2, 2, 2, 1, 1, 1, 1 }; // 默认5个水平条,随后修改 int H[] = { 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; #if 1 const int D[][2] = { 0, -1, 0, 1, -1, 0, 1, 0 }; #else const int D[][2] = { 0, 1, 0, -1, -1, 0, 1, 0 }; #endif enum { QMAX = 3800 * 10000 }; struct State { uint xs, ys; uint p; // 局面路径的previous void operator=(const char* s); void operator=(const State& that) { memcpy(this, &that, sizeof(*this)); } void print(const char* s = ""); } q[QMAX + 10 * 4]; int qh, qt = 1; // queue head, tail int a[10][2], b[5][4]; // 坐标与棋盘;int 比byte稍快 unsigned seen[1 << (30 - 5)]; // 30位y;32=1<<5 // 宏的名字和(间不能有空格 #define froma(s) for (int i = 0; i < 10; i++) { (s).xs = ((s).xs << 2) | a[i][1]; (s).ys = ((s).ys << 3) | a[i][0]; } // 修改全局变量b;调函数开销还是挺大; \后不能有注释 #define toary(s) { \ memset(b, 1, sizeof(b)); \ uint X = (s).xs, Y = (s).ys; \ for (int i = 9; i >= 0; i--) { \ const int& x = a[i][1] = X & 3; X >>= 2; \ const int& y = a[i][0] = Y & 7; Y >>= 3; \ for (int yy = y; yy < y + H[i]; yy++) \ for (int xx = x; xx < x + W[i]; xx++) \ b[yy][xx] = 0; \ } \ } void State::operator= (const char* s) { int p = 6; for (int x = 3; x >= 0; x--) for (int y = 4; y >= 0; y--) { #define CASE(c, i) case c: a[i][1] = x; a[i][0] = y; break; switch (s[x * 5 + y]) { CASE('c', 0) CASE('g', 1) // 曹关 CASE('z', 2) CASE('h', 3) // 张黄 CASE('l', 4) CASE('m', 5) // 子龙(l) 马 case 'p': a[p][1] = x; a[p++][0] = y; // pawn } } static const char* S[] = { "gg", "zz", "hh", "ll", "mm" }; for (int i = 0; i < 5; i++) if (strstr(s, S[i])) W[i+1] = 1, H[i+1] = 2; froma(*this); } void State::print (const char* s) { toary(*this); const char* b[5][4] = {}; int idx[10] = {}; for (int i = 0; i < 10; i++) { int x = a[i][1], y = a[i][0]; for (int yy = y; yy < y + H[i]; yy++) for (int xx = x; xx < x + W[i]; xx++) b[yy][xx] = NM[i][idx[i]++]; } for (int y = 0; y < 5; y++) { for (int x = 0; x < 4; x++) printf("%s", b[y][x] ? : " "); puts(""); } printf("%s\n", s); } int print_path (double tm) { // 用数组存放的单链表就地翻转 int prev = -1, next = -1, n = 0; for (int cur = qh; cur != -1;) { next = q[cur].p; q[cur].p = prev; prev = cur; cur = next; ++n; } for (int p = 0; p != -1; p = q[p].p) q[p].print(); return printf("%d %.6f ", n, tm); } #define peek(i) const uint key = q[i].xs ^ q[i].ys, ki = key >> 5, km = 1 << (key & 0x1f) #define aboo seen[ki] |= km int main (int argc, char* argv[]) { //q[0] = "pp zz""ccghh""ccgll""pp mm"; q[0] = (argc == 2) ? argv[1] : "pzzpg""cc g""ccllm""phhpm"; q[0].p = -1; peek(0); aboo; for (double tm = clock(); qh < qt; qh++) { toary(q[qh]); if (a[0][0] == 3) return print_path((clock() - tm) / CLOCKS_PER_SEC); // 不do ... while (0)了 #define add_state() { \ froma(q[qt]); \ peek(qt); \ if (seen[ki] & km) q[qt].xs = q[qt].ys = 0; \ else { aboo; q[qt++].p = qh; } \ } for (int j = 0; j < 4; j++) { int ok; for (int i = 0; i < 6; i++) { const int ox = a[i][1], oy = a[i][0]; int x = (a[i][1] += D[j][0]), y = (a[i][0] += D[j][1]); if (x < 0 || x + W[i] > 4 || y < 0 || y + H[i] > 5) ok = 0; else { if (j == 0) ok = b[y][x] & (!(W[i] - 1) | b[y][x + 1]); // W[i]1或2; 不更快 else if (j == 1) { y = oy + H[i]; ok = b[y][x] && (W[i] == 1 || b[y][x + 1]); } else if (j == 2) ok = b[y][x] & (!(H[i] - 1) | b[y + 1][x]); else { x = ox + W[i]; ok = b[y][x] && (H[i] == 1 || b[y + 1][x]); } } if (ok) add_state(); a[i][1] -= D[j][0]; a[i][0] -= D[j][1]; } for (int i = 6; i < 10; i++) { const int ox = a[i][1], oy = a[i][0]; int x = (a[i][1] += D[j][0]), y = (a[i][0] += D[j][1]); if (x < 0 || x + W[i] > 4 || y < 0 || y + H[i] > 5) ok = 0; else { if (j == 1) y = oy + H[i]; else if (j == 3) x = ox + W[i]; ok = b[y][x]; } if (ok) add_state(); a[i][1] -= D[j][0]; a[i][0] -= D[j][1]; } } } return printf("%d\n", qt); }
〔吕震宇的C#程序〕,Hash size不会大:512MB内存的电脑。CircularLinkedList.cs里的BeginProcess递归;不是最优解。算阶乘能递归也能循环,也许他的是BFS.
〔穷举一横四竖〕,Intel N100单线程11秒,易改12线程。五个长条中选k个横放,C(5,0)+...+C(5,5)=2**5。
〔 Modern perfect hashing〕〔 推箱子搜索〕
得意了不到两天,发现自己很土。 找得少比找得快更重要。对调块和方向的循环,直接干到0.155秒。 memset后再几乎全部覆盖?!空格也做成棋子。 醉心于压缩空间,其实3600万*24=864M; sizeof(有位域的struct)往往不小。 W*2+H当类型T。算hash时用T [5][4], 解决对称性。 检测移动时用另一个byte [5][4],元素是棋子的编号。移动空格。
操作空格太麻烦了。下面的版本只用了类型,0.21秒,最优解。可切换std::set和cwisstable
#include <stdio.h> #include <stdint.h> #include <string.h> #include <time.h> #include <immintrin.h> #include <xmmintrin.h> #include <set> #include "cwisstable.h" const char* NM[][4] = { "曹","贼","真","爽", {"西","施"}, {"昭","君"}, {"貂","蝉"}, {"甄","姬"}, {"玉","环"}, {"美"}, {"美"}, {"美"}, {"美"} }; int W[] = { 2, 2, 2, 2, 2, 2, 1, 1, 1, 1 }; // 默认5个水平条,随后修改 int H[] = { 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; int T[10]; // Type const int D[][2] = { 0, -1, 0, 1, -1, 0, 1, 0 }; // 后面用下标判断移动方向:别改! enum { MAX = 3600 * 10000 }; struct State { uint8_t a[10][2]; #define CCY a[0][0] // 曹操的y int p; // 局面路径的previous #define cpy20(dst, src) _mm_storeu_si128((__m128i*)dst, _mm_loadu_si128((__m128i*)src)); *(int*)((uint8_t*)dst+16) = *(int*)((const uint8_t*)src+16) void operator= (const State& s) { cpy20(a, s.a); p = s.p; } void operator=(const char* s); void print(const char* s = ""); } q[MAX + 10 * 4]; // 最多40个move. 不过MAX很大,而且没判断qt < MAX int qh, qt = 1; // queue head, tail void State::operator= (const char* s) { int p = 6; for (int x = 3; x >= 0; x--) for (int y = 4; y >= 0; y--) { #define CASE(c, i) case c: a[i][1] = x; a[i][0] = y; break; switch (s[x * 5 + y]) { // 曹关张黄子龙(l)马 CASE('c', 0) CASE('g', 1) CASE('z', 2) CASE('h', 3) CASE('l', 4) CASE('m', 5) case 'p': a[p][1] = x; a[p++][0] = y; // pawn } } static const char* S[] = { "gg", "zz", "hh", "ll", "mm" }; for (int i = 0; i < 5; i++) if (strstr(s, S[i])) W[i+1] = 1, H[i+1] = 2; for (int i = 0; i < 10; i++) T[i] = W[i] * 2 + H[i]; } #define set2DArrayByCoordInA(b, what) \ for (int i = 0; i < 10; i++) { \ const int x = a[i][1], y = a[i][0]; \ for (int yy = y; yy < y + H[i]; yy++) \ for (int xx = x; xx < x + W[i]; xx++) b[yy][xx] = what; \ } void State::print (const char* s) { int idx[10] = {}; const char* b[5][4] = {}; set2DArrayByCoordInA(b, NM[i][idx[i]++]) for (int y = 0; y < 5; y++) { for (int x = 0; x < 4; x++) printf("%s", b[y][x] ? : " "); puts(""); } printf("%s\n", s); } void print_path () { // 数组存的单链表就地翻转 int prev = -1, next = -1, n = 0; for (int cur = qh; cur != -1;) { next = q[cur].p; q[cur].p = prev; prev = cur; cur = next; ++n; } for (int p = 0; p != -1; p = q[p].p) q[p].print(); printf("%d\n", n); } struct TA { // Type Array uint8_t b[5][4]; const bool operator< (const TA& t) const { return memcmp(b, t.b, 20) < 0; } }; #define Ah TA ta __attribute__((aligned(32))), ta2; \ _mm256_store_si256((__m256i*)ta.b, _mm256_setzero_si256()); \ set2DArrayByCoordInA(ta.b, T[i]) #ifdef stl std::set<TA> set; #define add_state { cpy20(q[qt].a, a); \ Ah if (set.find(ta) == set.end()) { set.insert(ta); q[qt++].p = qh; } \ } #else CWISS_DECLARE_FLAT_HASHSET(Set, TA); Set set = Set_new(MAX); #define add_state { cpy20(q[qt].a, a); \ Ah if (!Set_contains(&set, &ta)) { Set_insert(&set, &ta); q[qt++].p = qh; } \ } #endif int main (int argc, char* argv[]) { //q[0] = "pp zz""ccghh""ccgll""pp mm"; q[0] = (argc == 2) ? argv[1] : "pzzpg""cc g""ccllm""phhpm"; q[0].p = -1; uint8_t a[10][2]; #ifdef stl Ah set.insert(ta); #else Ah Set_insert(&set, &ta); #endif double tm = clock(); for (; qh < qt; qh++) { if (q[qh].CCY == 3) { print_path(); break; } cpy20(a, q[qh].a); uint8_t b[5][4] __attribute__((aligned(32))), overflow[12]; _mm256_store_si256((__m256i*)b, _mm256_set1_epi8(1)); set2DArrayByCoordInA(b, 0) int qt1 = qt; for (int j = 0; j < 4; j++) { int ok; for (int i = 0; i < 6; i++) { const int ox = a[i][1], oy = a[i][0]; int x = (a[i][1] += D[j][0]), y = (a[i][0] += D[j][1]); if (x < 0 || x + W[i] > 4 || y < 0 || y + H[i] > 5) ok = 0; else { // D[]的顺序不能换! if (j == 0) ok = b[y][x] & (!(W[i] - 1) | b[y][x + 1]); // W[i]1或2; 不更快 else if (j == 1) { y = oy + H[i]; ok = b[y][x] && (W[i] == 1 || b[y][x + 1]); } else if (j == 2) ok = b[y][x] & (!(H[i] - 1) | b[y + 1][x]); else { x = ox + W[i]; ok = b[y][x] && (H[i] == 1 || b[y + 1][x]); } } if (ok) add_state a[i][1] -= D[j][0]; a[i][0] -= D[j][1]; } for (int i = 6; i < 10; i++) { const int ox = a[i][1], oy = a[i][0]; int x = (a[i][1] += D[j][0]), y = (a[i][0] += D[j][1]); if (x < 0 || x + W[i] > 4 || y < 0 || y + H[i] > 5) ok = 0; else { if (j == 1) y = oy + H[i]; else if (j == 3) x = ox + W[i]; ok = b[y][x]; } if (ok) add_state a[i][1] -= D[j][0]; a[i][0] -= D[j][1]; } } #if 1 int qt2 = qt - 1; if (qt2 > qt1 && q[qt2].CCY < q[qt1].CCY) { State t = q[qt1]; q[qt1] = q[qt2]; q[qt2] = t; } #endif } printf("%.6f %d\n", (clock() - tm) / CLOCKS_PER_SEC, qt); return 0; }
上述代码有bug,为了避免再犯,把State:a改成了aa. 〔0.02秒 最优解版〕
HTML看路径:

← ↑ → ↓前后。做“推鞋子”游戏?〔这里〕有很多。
1 <html lang="zh-CN"> 2 <head><meta charset="UTF-8"> 3 <style> 4 table { 5 position: absolute; 6 top: 50%; left: 50%; transform: translate(-50%, -50%); 7 border-collapse: collapse; 8 } 9 td { 10 width: 66px; height: 66px; 11 text-align: center; 12 font-size: 40px; 13 } 14 input, button { 15 position: absolute; 16 left: 50%; transform: translateX(-50%); 17 bottom: 40px; 18 font-size: 16pt; 19 } 20 input { display: none; } 21 button { padding: 8px; } 22 pre { display:none; } 23 img { padding: 0; margin: 0 } 24 </style></head><body> 25 <table id="tbl"></table> 26 <input type="file" id="file" accept=".txt"> 27 <button id="btn" onclick="file.click()" tabindex="-1">打开文件</button> 28 <script> 29 let d = document, lines, idx, pic 30 31 for (let r = 0; r < 5; r++) { 32 let row = d.createElement('tr') 33 for (let c = 0; c < 4; c++) row.appendChild(d.createElement('td')) 34 tbl.appendChild(row) 35 } 36 37 function draw () { 38 for (let i = 0; i < 5; i++) 39 for (let j = 0; j < 4; j++) { 40 let cell = tbl.rows[i].cells[j], s = lines[idx + i][j] 41 cell.textContent = s 42 let c = 'white' 43 if ('曹贼真爽'.includes(s)) c = 'gold' 44 else if ('西施'.includes(s)) c = 'forestgreen' 45 else if ('昭君'.includes(s)) c = 'orangered' 46 else if ('貂蝉'.includes(s)) c = 'blueviolet' 47 else if ('甄姬'.includes(s)) c = 'lightseagreen' 48 else if ('玉环'.includes(s)) c = 'hotpink' 49 else if (s === '美') cell.innerHTML = '<img src="' + pic + '">' 50 cell.style.background = c 51 } 52 } 53 54 file.addEventListener('change', (e)=>{ 55 let reader = new FileReader() 56 reader.onload = function(e) { lines = e.target.result.split('\n'); idx = 0; draw() } 57 if (e.target.files.length) reader.readAsText(e.target.files[0]) 58 }) 59 60 d.addEventListener('keydown', (e)=>{ 61 btn.blur() 62 let k = e.key 63 if (k === 'ArrowDown' || k === 'ArrowRight') { 64 if (idx + 12 < lines.length) idx += 6 65 draw() 66 } 67 else if (k === 'ArrowLeft' || k === 'ArrowUp') { 68 if (idx >= 6) idx -= 6; 69 draw() 70 } 71 }) 72 73 onload = ()=>{ pic = d.getElementsByTagName('pre')[0].innerHTML } 74 </script> 75 </body> 76 <pre>data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAtGVYSWZJSSoACAAAAAYAEgEDAAEAAAABAAAAGgEFAAEAAABWAAAAGwEFAAEAAABeAAAAKAEDAAEAAAACAAAAEwIDAAEAAAABAAAAaYcEAAEAAABmAAAAAAAAAEgAAAABAAAASAAAAAEAAAAGAACQBwAEAAAAMDIxMAGRBwAEAAAAAQIDAACgBwAEAAAAMDEwMAGgAwABAAAA//8AAAKgBAABAAAAQAAAAAOgBAABAAAAQAAAAAAAAABuEYT5AAADgUlEQVR4nO2a20tUURSHv5kmzFuZlhJBGRVhRUJX6X6DlKLIghKC6KGnoKDX/oBeCnzorR6jKBCpKJCiggoCEw26QjetLCMrsouVObFhTRyG0dnLGWfOOXM+WJwzw16cvX/7vvaGgICAgABXEALyM/3RMO4hAtTksgADQEUuCxAFynJZgBBQkssCFI1wEJzgFwGKRyhAnV8EmCTjgLbVTPWLAJXAT6XPJqDTLwJMB34pfXYCT/wiwCzgsyL9eGAL8MwvAiwE3itr36Tv94MAYaAa6FH47AGepuPDbmCOTIPdlunNyL8GeOwXAVbJFPjWMv1uyfsDPwnQo5gFdsnzIT6hE2ixTFsODAJ/gUI/tIBKYBrQYZl+o2ycXgDf/SDAennetUy/QZ4p93+3CNAgwZDrlukX+6n/T5HC31L49MqMYYTzfAtoAMYA5yzTFwCl8m4GQs/TBvxRxAIrpPaNHcTjzJWCXFD4lDsEsJ02XcsxKUi9wmeyQwDTBWbjUSLAOxnQ8hR+ixwCGGvEo2yVApxQ+h2OE+A3UIUHaZYCLFH63Y4TwNg1PEa51Jx2ITNf+n00gW3HQ+yXTB9R+p0ZovDGWvEQlyXT8xQ+S4ep/ZiZDZXrKZLQ9yul38UkhTe22QtL4TpgnBTIlirLwhV6QYBt8ryk8Dlgmc8uXE5YFj5fFYufscAHi+bfIUESV7NAMtuk8FlnUfgBR4zA1V1gpWMxozktSsYp4J4XBFghT82cbbPM1S6ns0aX7P01o/WdJM0/pYPRTFItGW5X+JSJYIn6fOz9bKoZC5O53V9sE2RLvWyb4znueH+DR2gFPipugYUlXBZf+y1yjyD2+xAeoBj4BKxV+DQO0edr4yLDmmhSxskDlgOr5fTHNuh5epj53ogZO0SJyiYpJSKkH7Mi2ysxP2eT75fjrJeyGeqWmgxJrK9GWokJfSfiEdAn78+BZW4cA6qAGxart5HYScd3jsp/+W6ZBQokU/eVfV3DTcd7r3QJ7a2yUaFWmnZ0FG0w7vBkH/CFLFOSJEyVTmtPcKT2Oh2FiIzQr1QONM3pTiY4H/fbXI74RhbZkaGaN3Y1QfzAXJK4QpZje01DrNXTZX0SOR6Nqfo/oRT9zW3NmcAMh1U63jXTVJ8Mpm0ylTan4wpMMkKj/QFgogRC82XgDMnBSKxwPyRMZp4BAQEBAWSQf0M3aYd8JHYVAAAAAElFTkSuQmCC</pre> 77 </html>

浙公网安备 33010602011771号