ACWing-186巴士-DFS

题目

原题链接:186. 巴士 - AcWing题库

oj:xmuoj | 最小化蒙德城的旅行者队伍

思路

  1. 本题因为n=300,说明搜索深度很深,但是测试数量小于17,所以答案一定在比较浅的一个搜索深度中,于是可以利用迭代加深的处理方法
  2. 迭代加深:每次深搜都会有搜索的最大深度限制,如果没有找到解,那么就增大深度,再进行深搜,如此循环直到找到解为止,这样可以找到最浅层的解。

    迭代加深搜索(IDDFS)的原理与练习 - 知乎 (zhihu.com)
  3. 先预处理出所有合法的路线:首项a,公差d;
    条件:0-a-1没有车;a-d<0==》d>=a+1:因为a是第一辆车;a+d<60;
  4. 最少选择多少条路线,可以将所有的公交车覆盖:组合式枚举
  5. 剪枝:
    1. dfs时需要传入一个枚举的起点:按照组合式的枚举方式
    2. 优先枚举点较多的路线==》尽可能选择分支数比较少的路径来搜索,优化搜索顺序
    3. 如果当前路线上的点数*剩余可以选择的路线数量+现在已经覆盖的公交车数量<总的公交车车数:可行性剪枝
       

代码

#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:d
        for (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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值