[关闭]
@LittleRewriter 2018-07-15T14:09:06.000000Z 字数 10405 阅读 808

lyd搜索专题

搜索


深搜及其优化

本节和lyd的顺序略错位。

深搜的含义与基本应用(0x22)

深搜的定义

深搜是一类包括遍历形式、状态记录与检索、剪枝优化等算法整体设计的统称。

深搜会形成一颗搜索树。我们将问题空间中的状态抽象成一个点,将访问两个状态的移动抽象为一条边,形成的树是搜索树。

整个深搜算法基于搜索树完成,为了避免重复访问,我们对状态进行记录检索;为了使程序更加高效,我们进行剪枝。

小猫爬山

...第一反应是状压DP我还有救么

状态是第now只猫使用了cnt架缆车,暴力枚举即可。

最优化剪枝是显然的;至于为什么先要从大到小排序?这样可以减少枚举次数。

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. using namespace std;
  5. int cat[20], lc[20], W, n, ans;
  6. bool cmp(int a, int b) {return a > b;}
  7. void dfs(int cnt, int now) {
  8. if(cnt >= ans) return;
  9. if(now > n) {
  10. ans = min(ans, cnt);
  11. return;
  12. }
  13. for(int i = 1; i <= cnt; ++i) {
  14. if(lc[i] + cat[now] <= W) {
  15. lc[i] += cat[now];
  16. dfs(cnt, now + 1);
  17. lc[i] -= cat[now];
  18. }
  19. }
  20. lc[cnt + 1] += cat[now];
  21. dfs(cnt + 1, now + 1);
  22. lc[cnt + 1] -= cat[now];
  23. }
  24. int main() {
  25. scanf("%d%d", &n, &W);
  26. for(int i = 1; i <= n; ++i) scanf("%d", &cat[i]);
  27. ans = n;
  28. sort(cat + 1, cat + 1 + n, cmp);
  29. dfs(0, 1);
  30. printf("%d", ans);
  31. return 0;
  32. }

数独型搜索

luogu1784数独、POJ2676、POJ3074、luogu1074靶形数独、POJ3076

数据难度是递增的。。这次我们将数独型搜索的重点放在优化剪枝上。所以我先贴一下我好多年以前开O2才A掉的朴素靶形数独。

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cctype>
  5. using namespace std;
  6. int map[11][11];
  7. int tensu[10][10] =
  8. {
  9. {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  10. {0, 6, 6, 6, 6, 6, 6, 6, 6, 6},
  11. {0, 6, 7, 7, 7, 7, 7, 7, 7, 6},
  12. {0, 6, 7, 8, 8, 8, 8, 8, 7, 6},
  13. {0, 6, 7, 8, 9, 9, 9, 8, 7, 6},
  14. {0, 6, 7, 8, 9, 10,9, 8, 7, 6},
  15. {0, 6, 7, 8, 9, 9, 9, 8, 7, 6},
  16. {0, 6, 7, 8, 8, 8, 8, 8, 7, 6},
  17. {0, 6, 7, 7, 7, 7, 7, 7, 7, 6},
  18. {0, 6, 6, 6, 6, 6, 6, 6, 6, 6}
  19. };
  20. int dijige[10][10] =
  21. {
  22. {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  23. {0, 1, 1, 1, 2, 2, 2, 3, 3, 3},
  24. {0, 1, 1, 1, 2, 2, 2, 3, 3, 3},
  25. {0, 1, 1, 1, 2, 2, 2, 3, 3, 3},
  26. {0, 4, 4, 4, 5, 5, 5, 6, 6, 6},
  27. {0, 4, 4, 4, 5, 5, 5, 6, 6, 6},
  28. {0, 4, 4, 4, 5, 5, 5, 6, 6, 6},
  29. {0, 7, 7, 7, 8, 8, 8, 9, 9, 9},
  30. {0, 7, 7, 7, 8, 8, 8, 9, 9, 9},
  31. {0, 7, 7, 7, 8, 8, 8, 9, 9, 9}
  32. };
  33. bool geneivis[11][11],hangvis[11][11], lievis[11][11];
  34. inline void read(int& x) {
  35. x = 0; char c = getchar();
  36. while(!isdigit(c)) c = getchar();
  37. while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
  38. }
  39. int xx, yy, maxn = -1, ori;
  40. void printmap() {
  41. for(int i = 1; i <= 9; ++i) {
  42. for(int j = 1; j <= 9; ++j) {
  43. printf("%d", map[i][j]);
  44. }
  45. printf("\n");
  46. }
  47. printf("\n");
  48. }
  49. void printvis() {
  50. printmap();
  51. for(int i = 1; i <= 9; ++i) {
  52. for(int j = 1; j <= 9; ++j) {
  53. cout<<hangvis[i][j]<<" ";
  54. }
  55. cout<<endl;
  56. }
  57. cout<<endl;
  58. for(int i = 1; i <= 9; ++i) {
  59. for(int j = 1; j <= 9; ++j) {
  60. cout<<hangvis[i][j]<<" ";
  61. }
  62. cout<<endl;
  63. }
  64. cout<<endl;
  65. for(int i = 1; i <= 9; ++i) {
  66. for(int j = 1; j <= 9; ++j) {
  67. cout<<geneivis[i][j]<<" ";
  68. }
  69. cout<<endl;
  70. }
  71. cout<<endl;
  72. }
  73. inline void dfs(register int x, register int y, register int ans, register int mark) {
  74. if(ans + mark * 90 <= maxn) return;
  75. //cout<<x<<" "<<y<<" "<<ans<<endl;
  76. //if(x < 1 || y < 1 || x > 9 || y > 9) return;
  77. if(x == 0) {
  78. //printmap();
  79. maxn = max(ans, maxn);
  80. return;
  81. }
  82. if(!map[x][y]){
  83. for(register int i = 9; i >= 1; --i) {
  84. //if(xx < 1 || yy < 1 || xx > 9 || yy > 9) continue;
  85. //if(x == 7 && y == 9)printvis();
  86. if(!hangvis[x][i] && !lievis[y][i] && !geneivis[dijige[x][y]][i]) {
  87. hangvis[x][i] = 1, lievis[y][i] = 1, geneivis[dijige[x][y]][i] = 1;
  88. map[x][y] = i;
  89. //cout<<"i:"<<i<<" "<<x<<" "<<y<<endl;
  90. //printmap();
  91. if(y > 1) dfs(x, y-1, ans + tensu[x][y] * i, mark - 1);
  92. else dfs(x-1, 9, ans + tensu[x][y] * i, mark - 1);
  93. hangvis[x][i] = 0, lievis[y][i] = 0, geneivis[dijige[x][y]][i] = 0;
  94. map[x][y] = 0;
  95. }
  96. }
  97. }else {
  98. if(y > 1) dfs(x, y-1, ans + tensu[x][y] * map[x][y], mark - 1);
  99. else dfs(x-1, 9, ans + tensu[x][y] * map[x][y], mark - 1);
  100. }
  101. return;
  102. }
  103. int main() {
  104. //freopen("test.txt", "w", stdout);
  105. for(int i = 1; i <= 9; ++i) {
  106. for(int j = 1; j <= 9; ++j) {
  107. read(map[i][j]);
  108. }
  109. }
  110. for(int i = 1; i <= 9; ++i) {
  111. for(int j = 1; j <= 9; ++j) {
  112. if(map[i][j]) {
  113. hangvis[i][map[i][j]] = 1;
  114. lievis[j][map[i][j]] = 1;
  115. geneivis[dijige[i][j]][map[i][j]] = 1;
  116. }
  117. }
  118. }
  119. dfs(9, 9, 0, 81);
  120. printf("%d\n", maxn);
  121. return 0;
  122. }

...是不是觉得非常壮观。这道题非常的经典,因此我联赛前专门做了它锻炼码力,虽说毫无卵用。

这道题有一个玄学优化:就是倒着搜。这样做的原因是出题人会专门卡正着搜;但是即便如此,除非结合卡时,否则还是会T在11点。即便开了O2,11点还是跑了956ms。可见非常玄学。

所以我们迫切的需要一些不那么玄学的剪枝来A掉。当然,有种数据结构叫做Dancing links可以解决这类问题;但是我个人是很讨厌数据结构而且这个东西不好学,所以我们不做讨论。

首先,我们应当有一个原则,就是从可能性最小的分支开始搜索。我们可以在一开始就扫一遍,把能填上的先填上;然后考虑每一行、每一列、每个九宫格,看看能不能填。

其次,对于某些点,我们可以用位运算优化。将行列九宫格的数用九位二进制数保存,这样的话按位与之后得到的结果就是可填的东西,枚举起来非常方便。

然后luogu那道题我们就可以这么0msA掉了

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. using namespace std;
  5. #define ri register int
  6. int belong[9][9] = {
  7. {0, 0, 0, 1, 1, 1, 2, 2, 2},
  8. {0, 0, 0, 1, 1, 1, 2, 2, 2},
  9. {0, 0, 0, 1, 1, 1, 2, 2, 2},
  10. {3, 3, 3, 4, 4, 4, 5, 5, 5},
  11. {3, 3, 3, 4, 4, 4, 5, 5, 5},
  12. {3, 3, 3, 4, 4, 4, 5, 5, 5},
  13. {6, 6, 6, 7, 7, 7, 8, 8, 8},
  14. {6, 6, 6, 7, 7, 7, 8, 8, 8},
  15. {6, 6, 6, 7, 7, 7, 8, 8, 8}
  16. };
  17. int sodoku[10][10], row[10], col[10], gri[10], tot;
  18. int cnt[1 << 10], num[1 << 10];
  19. inline void cha(int x, int y, int z) {
  20. row[x] ^= 1 << z;
  21. col[y] ^= 1 << z;
  22. gri[ belong[x][y] ] ^= 1 << z;
  23. }
  24. inline bool dfs(int now) {
  25. if(!now) return 1;
  26. ri val, tag = 10; int x, y; //tag表示可能性,可能性不可能超过10个
  27. for(ri i = 0; i < 9; ++i) {
  28. for(ri j = 0; j < 9; ++j) { //选取可能性最低的点
  29. //cout<<i<<" "<<j<<" "<<sodoku[i][j]<<endl;
  30. if(sodoku[i][j] != 0) continue;
  31. val = row[i] & col[j] & gri[ belong[i][j] ];
  32. //if(now == 60) cout<<i<<" "<<j<<" "<<row[i]<<" "<<col[j]<<" "<<belong[i][j]<<" "<<gri[ belong[i][j] ]<<endl;
  33. if(!val) return 0; //如果某一个点不能放,该决策错误
  34. if(cnt[val] < tag) {
  35. tag = cnt[val];
  36. x = i, y = j;
  37. }
  38. }
  39. }
  40. val = row[x] & col[y] & gri[ belong[x][y] ];
  41. ri tmp;
  42. for(int qwq = val; qwq; qwq -= qwq & -qwq) {
  43. tmp = num[qwq & -qwq];
  44. sodoku[x][y] = tmp + 1;
  45. cha(x, y, tmp);
  46. if(dfs(now - 1)) return 1;
  47. cha(x, y, tmp);
  48. sodoku[x][y] = 0;
  49. }
  50. return 0;
  51. }
  52. int main() {
  53. for(int i = 0; i < 9; ++i)
  54. row[i] = col[i] = gri[i] = (1 << 9) - 1,
  55. num[1 << i] = i;
  56. for(int i = 0; i < 1 << 9; ++i)
  57. for(int j = i; j; j -= j&-j) cnt[i]++;
  58. for(int i = 0; i < 9; ++i)
  59. for(int j = 0; j < 9; ++j)
  60. cin>>sodoku[i][j];
  61. for(int i = 0; i < 9; ++i) {
  62. for(int j = 0; j < 9; ++j) {
  63. if(sodoku[i][j]) cha(i, j, sodoku[i][j] - 1);
  64. else ++tot;
  65. }
  66. }
  67. /*for(int i = 0; i < (1 << 9); ++i) cout<<cnt[i]<<" ";
  68. puts("");
  69. for(int i = 0; i < (1 << 9); ++i) cout<<num[i]<<" ";
  70. puts("");
  71. for(int i = 0; i < 9; ++i) cout<<row[i]<<" ";
  72. puts("");
  73. for(int i = 0; i < 9; ++i) cout<<col[i]<<" ";
  74. puts("");
  75. for(int i = 0; i < 9; ++i) cout<<gri[i]<<" ";
  76. puts("");*/
  77. dfs(tot);
  78. for(int i = 0; i < 9; ++i) {
  79. for(int j = 0; j < 9; ++j)
  80. cout<<sodoku[i][j]<<" ";
  81. cout<<endl;
  82. }
  83. return 0;
  84. }

。。至于POJ3076什么的无能为力我直接扔lyd写了6k的代码了

  1. #include <stdio.h>
  2. #define EMPTY -1
  3. char map[16][16];
  4. unsigned short table[16][16];
  5. int filled;
  6. void fill (int i, int j, int a)
  7. {
  8. int r, c, k, m;
  9. filled++;
  10. map[i][j] = a;
  11. for (k = 0; k < 16; k++)
  12. table[i][k] |= (1 << (a - 1)), table[k][j] |= (1 << (a - 1));
  13. r = (int)(i / 4) * 4;
  14. c = (int)(j / 4) * 4;
  15. for (k = 0; k < 4; k++)
  16. {
  17. for (m = 0; m < 4; m++)
  18. table[r + k][c + m] |= (1 << (a - 1));
  19. }
  20. return;
  21. }
  22. int search ()
  23. {
  24. char t_map[16][16];
  25. unsigned short t_table[16][16]; int t_filled;
  26. int i, j, k, m, r, c, r1, r2, t0, t, flag;
  27. if (filled == 256) return 1;
  28. for (i = 0; i < 16; i++)
  29. {
  30. for (j = 0; j < 16; j++)
  31. {
  32. if (map[i][j]) continue;
  33. r = -1;
  34. for (k = 0; k < 16; k++)
  35. {
  36. if (((table[i][j] & (1 << k)) == 0) && r == -1) r = k;
  37. else if (((table[i][j] & (1 << k)) == 0) && r != -1) { r = -2; break; }
  38. }
  39. if (r == -1) return 0;
  40. else if (r != -2) fill(i, j, r + 1);
  41. }
  42. }
  43. for (i = 0; i < 16; i++)
  44. {
  45. for (k = 0; k < 16; k++)
  46. {
  47. r = -1;
  48. for (j = 0; j < 16; j++)
  49. {
  50. if (map[i][j] == k + 1) { r = -2; break; }
  51. if (map[i][j]) continue;
  52. if (((table[i][j] & (1 << k)) == 0) && r == -1) r = j;
  53. else if (((table[i][j] & (1 << k)) == 0) && r != -1) { r = -2; break; }
  54. }
  55. if (r == -1) return 0;
  56. else if (r != -2) fill(i, r, k + 1);
  57. }
  58. }
  59. for (j = 0; j < 16; j++)
  60. {
  61. for (k = 0; k < 16; k++)
  62. {
  63. r = -1;
  64. for (i = 0; i < 16; i++)
  65. {
  66. if (map[i][j] == k + 1) { r = -2; break; }
  67. if (map[i][j]) continue;
  68. if (((table[i][j] & (1 << k)) == 0) && r == -1) r = i;
  69. else if (((table[i][j] & (1 << k)) == 0) && r != -1) { r = -2; break; }
  70. }
  71. if (r == -1) return 0;
  72. else if (r != -2) fill(r, j, k + 1);
  73. }
  74. }
  75. for (r = 0; r < 16; r += 4)
  76. {
  77. for (c = 0; c < 16; c += 4)
  78. {
  79. for (k = 0; k < 16; k++)
  80. {
  81. r1 = -1, r2 = -1;
  82. for (i = 0; i < 4; i++)
  83. {
  84. for (j = 0; j < 4; j++)
  85. {
  86. if (map[r + i][c + j] == k + 1) { r1 = r2 = -2; goto outerloop1; }
  87. if (map[r + i][c + j]) continue;
  88. if (((table[r + i][c + j] & (1 << k)) == 0) && r1 == -1) r1 = r + i, r2 = c + j;
  89. else if (((table[r + i][c + j] & (1 << k)) == 0) && r1 != -1)
  90. { r1 = r2 = -2; goto outerloop1; }
  91. }
  92. }
  93. outerloop1:
  94. if (r1 == -1) return 0;
  95. else if (r1 != -2) fill(r1, r2, k + 1);
  96. }
  97. }
  98. }
  99. if (filled == 256) return 1;
  100. for (i = 0; i < 16; i++)
  101. {
  102. for (j = 0; j < 16; j++)
  103. t_map[i][j] = map[i][j];
  104. }
  105. for (i = 0; i < 16; i++)
  106. {
  107. for (j = 0; j < 16; j++)
  108. t_table[i][j] = table[i][j];
  109. }
  110. t_filled = filled;
  111. t0 = 256;
  112. for (i = 15; i >= 0; i--)
  113. {
  114. for (j = 15; j >= 0; j--)
  115. {
  116. if (map[i][j]) continue;
  117. t = 0;
  118. for (k = 0; k < 16; k++)
  119. {
  120. if ((table[i][j] & (1 << k)) == 0) t++;
  121. if (t >= t0) break;
  122. }
  123. if (t < t0)
  124. {
  125. t0 = t;
  126. r1 = i, r2 = j;
  127. }
  128. }
  129. }
  130. outerloop2:
  131. for (m = 0; m < 16; m++)
  132. {
  133. if ((table[r1][r2] & (1 << m)) == 0)
  134. {
  135. fill(r1, r2, m + 1);
  136. flag = search();
  137. if (flag == 1) return 1;
  138. else
  139. {
  140. for (i = 0; i < 16; i++)
  141. {
  142. for (j = 0; j < 16; j++)
  143. map[i][j] = t_map[i][j];
  144. }
  145. for (i = 0; i < 16; i++)
  146. {
  147. for (j = 0; j < 16; j++)
  148. table[i][j] = t_table[i][j];
  149. }
  150. filled = t_filled;
  151. }
  152. }
  153. }
  154. return 0;
  155. }
  156. int main ()
  157. {
  158. int i, j, k, m, r, c, a;
  159. char ar[60];
  160. while (1)
  161. {
  162. memset(map, 0, sizeof(map));
  163. memset(table, 0, sizeof(table));
  164. filled = 0;
  165. for (i = 0; i < 16; i++)
  166. {
  167. if (scanf("%s", ar) != 1) goto end;
  168. for (j = 0; j < 16; j++)
  169. {
  170. a = ar[j];
  171. if (a == '-') continue;
  172. else fill(i, j, a - 64);
  173. }
  174. }
  175. search();
  176. for (i = 0; i < 16; i++)
  177. {
  178. for (j = 0; j < 16; j++)
  179. printf("%c", map[i][j] + 64);
  180. printf("\n");
  181. } printf("\n");
  182. }
  183. end: return 0;
  184. }

剪枝(0x23)

有六种常用剪枝方法。

第一种是卡时。卡时对最优化的深搜问题的非常有效的。

第二种是优化搜索顺序。我们可以去搜索玄学意义上更好的分支来达到效果;一般来说,可能性越小的分支应该越早的搜索。

第三章是排除等效冗余。下面会见到,一般来说对于重复子问题可以说这是很好用的。

第四种是可行性剪枝。如果说一个分支届到了边界,或者是不可能到达边界,我们就直接剪掉。比如刚才我们在数独型搜索中剪的那个。

第五种是最优性剪枝。比如在靶形数独的时候,我们搜到一个状态大于当前的最小状态,我们可以直接扔掉。

最后一种是记忆化。

POJ1011 Sticks

我们考虑这样一个状态设计。设木棒长度为k,那么,换言之我们的枚举范围是sum的所有约数。

我们的搜索状态是三维的代表第now个容器在对第wit进行搜寻,并且已经放了cap个。

先排序。

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cstring>
  5. #include <vector>
  6. using namespace std;
  7. int n, stick[80], sum, mas;
  8. vector<int> v;
  9. int cer[80];
  10. bool vis[80];
  11. bool cmp(int a, int b) {
  12. return a > b;
  13. }
  14. bool dfs(int val, int cap, int wit, int now) {
  15. //if(val == 227) cout<<cap<<" "<<wit<<" "<<now<<endl;
  16. if(now > (sum / val)) {
  17. return true;
  18. }
  19. if(cap == val) return dfs(val, 0, 1, now + 1);
  20. int fail = 0;
  21. for(int i = wit; i <= n; ++i) { /*第一处*/
  22. if(!vis[i] && cap + stick[i] <= val && stick[i] != fail) {
  23. vis[i] = 1;
  24. if(dfs(val, cap + stick[i], i + 1, now))
  25. return true;
  26. fail = stick[i]; /*第二处*/
  27. vis[i] = 0;
  28. if(cap == 0) return false; /*第三处*/
  29. }
  30. }
  31. return false;
  32. }
  33. int main() {
  34. //freopen("biao.txt", "w", stdout);
  35. while(cin>>n) {
  36. if(!n) break; sum = 0; mas = 0;
  37. for(int i = 1; i <= n; ++i)
  38. scanf("%d", &stick[i]), sum += stick[i], mas = max(mas, stick[i]);
  39. sort(stick + 1, stick + 1 + n, cmp);
  40. //for(int i = 1; i <= n; ++i) cout<<stick[i]<<" ";
  41. int ans;
  42. for(int i = mas; i <= sum; ++i) {
  43. //cout<<i<<" ";
  44. if(sum % i) continue;
  45. memset(vis, 0, sizeof vis);
  46. if(dfs(i, 0, 1, 1)) {
  47. ans = i;
  48. break;
  49. }
  50. }
  51. cout<<ans<<endl;
  52. }
  53. }

关注我专门圈住的三处。

第一处的含义是,对于递减排列的棍子来说,某一个格不能放就代表这个格以前的都不能放。

第二处的含义是,如果在搜索某一状态时有相同的a,b两个木棍且a不能放那么b一定不能放。

第三处的含义是,如果一个也放不进去,就说明该状态一定会出锅,直接减掉。

luogu1371/POJ1190/NOI1999 生日蛋糕

很棒的搜索题。

首先,我们从下往上倒序递归。

当然有个结论:所有的表面积=最下层蛋糕的表面积+所有层侧面积。

我们制定状态表示在搜索dep层时候的外表面积为S、体积为V。那么我们可以发现:

1,搜索范围

对于当前状态而言,首先搜索范围一定是

考虑到对于当前状态有,显然有,然后就得到了第一个剪枝。

注意这里搜索应该倒序搜索。

2,可行性剪枝

假定对于第层,我们预处理一个最小的表面积和体积,维护h与r均取时前dep层达到的最小值。如果就代表不可行。

3,最优性剪枝

这个略复杂...

首先如果已经比搜到的答案大我们直接剪枝。

考虑公式。我们知道从1~dep - 1层的体积有,表面积为

我们稍微变形一下。考虑到

因此我们直接比较与当前最优解即可。

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cstring>
  5. #include <cmath>
  6. #include <vector>
  7. using namespace std;
  8. typedef long long LL;
  9. const LL INF = 0x3f3f3f;
  10. LL smin[18], vmin[18];
  11. LL pr[18], ph[18];
  12. LL N, M, ans = INF;
  13. void preope() {
  14. for(int i = 1; i <= 16; ++i)
  15. smin[i] = smin[i - 1] + 2 * i * i,
  16. vmin[i] = vmin[i - 1] + i * i * i;
  17. pr[M + 1] = ph[M + 1] = INF;
  18. }
  19. void dfs(LL cnt, LL V, LL S) {
  20. if(cnt < 1) {
  21. if(V == N) ans = min(ans, S);
  22. return;
  23. }
  24. if(vmin[cnt] + V > N) return;
  25. if(S + smin[cnt] > ans) return;
  26. if(2 * (N - V) / pr[cnt + 1] + S > ans) return;
  27. for(LL r = min((LL)sqrt(1.0 * N - V), pr[cnt + 1] - 1) ; r >= cnt; --r) {
  28. for(LL h = min((N - V - vmin[cnt - 1]) / r / r, ph[cnt + 1] - 1); h >= cnt; --h) {
  29. pr[cnt] = r, ph[cnt] = h;
  30. if(cnt == M) S = r * r;
  31. dfs(cnt - 1, V + r * r * h, S + 2 * r * h);
  32. }
  33. }
  34. }
  35. int main() {
  36. cin>>N>>M;
  37. preope();
  38. dfs(M, 0, 0);
  39. if(ans == INF) return puts("0"), 0;
  40. cout<<ans<<endl;
  41. return 0;
  42. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注