题目
原题链接:186. 巴士 - AcWing题库
oj:xmuoj | 最小化蒙德城的旅行者队伍
思路
- 本题因为n=300,说明搜索深度很深,但是测试数量小于17,所以答案一定在比较浅的一个搜索深度中,于是可以利用迭代加深的处理方法
- 迭代加深:每次深搜都会有搜索的最大深度限制,如果没有找到解,那么就增大深度,再进行深搜,如此循环直到找到解为止,这样可以找到最浅层的解。
迭代加深搜索(IDDFS)的原理与练习 - 知乎 (zhihu.com) - 先预处理出所有合法的路线:首项a,公差d;
条件:0-a-1没有车;a-d<0==》d>=a+1:因为a是第一辆车;a+d<60; - 最少选择多少条路线,可以将所有的公交车覆盖:组合式枚举
- 剪枝:
- dfs时需要传入一个枚举的起点:按照组合式的枚举方式
- 优先枚举点较多的路线==》尽可能选择分支数比较少的路径来搜索,优化搜索顺序
- 如果当前路线上的点数*剩余可以选择的路线数量+现在已经覆盖的公交车数量<总的公交车车数:可行性剪枝
代码
#include <iostream>
#include <algorithm>
#include <vector>using namespace std;typedef pair<int, int> PII;const int N = 2000, M = 60;int n;
vector<pair<int, PII>> routes;//所有合法的路线
int bus[M]; //所有公交车的数量bool is_route(int a, int d)
{for (int i = a; i < 60; i += d)if (!bus[i]) //中间某一个时刻是没有公交车的,说明不合法return false;return true;
}bool dfs(int depth, int u, int sum, int start)//当前迭代层数,当前已经选择的公交车路线,当前已经覆盖的公交车的数量,开始值:从哪个公交车开始枚举
{if (u == depth) return sum == n; //当前剩余的层数等于当前剩余的公交车if (routes[start].first * (depth - u) + sum < n) return false;//可行性剪枝// 枚举选哪个路线for (int i = start; i < routes.size(); i ++ ){auto r = routes[i];int a = r.second.first, d = r.second.second;if (!is_route(a, d)) continue;for (int j = a; j < 60; j += d) bus[j] -- ;if (dfs(depth, u + 1, sum + r.first, i)) return true;for (int j = a; j < 60; j += d) bus[j] ++ ;//恢复现场}return false;
}int main()
{scanf("%d", &n);for (int i = 0; i < n; i ++ ){int t;scanf("%d", &t);bus[t] ++ ;}for (int i = 0; i < 60; i ++ )//i:a,j:dfor (int j = i + 1; i + j < 60; j ++ )if (is_route(i, j)) //判断是否合法routes.push_back({(59 - i) / j + 1, {i, j}});//第一个关键字:路线上的公交车数量,第二个关键字是(a,d)sort(routes.begin(), routes.end(), greater<pair<int, PII>>());//从大到小进行排序int depth = 0;while (!dfs(depth, 0, 0, 0)) depth ++ ;//迭代加深的过程;如果当前的不合法,则加深printf("%d\n", depth);return 0;
}