算法思想一:暴力法

解题思路:

对于求解本题牛妹最多能到多少个城市去旅游,我们可以直接模拟牛妹的访问过程,首先我们定义city,dp,flag数组,分别记录城市之间的次序关系,每个城市的前序访问和该城市有无被访问过。然后我们开始进行模拟访问,首先没有前序城市的城市进入队列,每次花费最小或者下标最小的城市出队,然后将相邻的后续城市的前序数量减一。最后我们可以遍历完所有城市,并且根据花费,计算出牛妹最多可以到达多少个城市去旅游。

整个过程使用了拓扑排序的思想

图解:

代码展示:

C++版本

/**
 * struct Point {
 *    int x;
 *    int y;
 *    Point(int xx, int yy) : x(xx), y(yy) {}
 * };
 */
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param N int整型 N
     * @param V int整型 V
     * @param A int整型vector A
     * @param list Point类vector List
     * @return int整型
     */
    struct node{
        int val;
        int index;
        bool friend operator < (node a, node b) // 根据本题目重载小于号
        {   //花费优先,然后是下标
            if(a.val == b.val)
                return a.index > b.index;
            return a.val > b.val;
        }
    };
    int Travel(int N, int V, vector<int>& A, vector<Point>& list) {
        // write code here
        vector dp(N + 1, 0); // 记录有多少个前序城市
        int res = 0; // 声明结果
        vector > city(N + 1, vector ()); // 记录城市之间的次序
        for(int i = 0; i < list.size(); i++)
        {
            city[list[i].x].push_back(list[i].y); // 城市入队
            dp[list[i].y]++;
        }
        priority_queue q;
        vector flag(N + 1, false); // 声明flag数组来记录该城市是否被访问
        for(int i = 1; i <= N; i++)
        {
            if(dp[i] == 0)
            {
                q.push({A[i - 1], i}); // 没有前序的城市可以直接放入队列,优先访问花费最小的
                flag[i] = true; // 记录访问过该点
            }
        }
        while(!q.empty())
        {
            node temp = q.top();
            q.pop();
            if(temp.val > V) // 预算不够了
                break;
            V -= temp.val;
            res++;
            for(int i = 0; i < city[temp.index].size(); i++) // 该城市的所有后序城市的前序数量减一
                dp[city[temp.index][i]]--;
            for(int i = 1; i <= N; i++)
            {   //遍历每个城市,如果该城市的前序为0且没有被访问过
                if(dp[i] == 0 && !flag[i])
                {
                    q.push({A[i - 1], i});
                    flag[i] = true;
                }
            }
        }
        return res; // 返回结果
    }
};

复杂度分析

时间复杂度:两层循环加一个队列时间

空间复杂度:使用额外数组,因此空间复杂度为

算法思想二:方法一优化

解题思路:

基于方法一,发现每次dp数组中的元素归零时,它一定还没有被访问过,同时它又只能收到前序城市的影响,因此可以将其直接加入队列中,从而省去flag数组,继而对代码进行优化。

图解(流程和方法一是一样的):

代码展示:

C++版本

/**
 * struct Point {
 *    int x;
 *    int y;
 *    Point(int xx, int yy) : x(xx), y(yy) {}
 * };
 */
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param N int整型 N
     * @param V int整型 V
     * @param A int整型vector A
     * @param list Point类vector List
     * @return int整型
     */
    struct node{
        int val;
        int index;
        bool friend operator < (node a, node b) // 根据本题目重载小于号
        {   //花费优先,然后是下标
            if(a.val == b.val)
                return a.index > b.index;
            return a.val > b.val;
        }
    };
    int Travel(int N, int V, vector<int>& A, vector<Point>& list) {
        // write code here
        vector dp(N + 1, 0); // 记录有多少个前序城市
        int res = 0; // 记录结果
        vector > city(N + 1, vector ()); //记录城市之间的次序
        for(int i = 0; i < list.size(); i++)
        {
            city[list[i].x].push_back(list[i].y); // 对城市进行判断
            dp[list[i].y]++;
        }
        priority_queue q;
        for(int i = 1; i <= N; i++)
            if(dp[i] == 0)
                q.push({A[i - 1], i});
        while(!q.empty())
        {
            node temp = q.top();
            q.pop();
            if(temp.val > V) // 预算不够了
                break;
            V -= temp.val;
            res++;
            for(int i = 0; i < city[temp.index].size(); i++)
            {   //该城市的所有后序城市的前序数量减一
                dp[city[temp.index][i]]--;
                if(dp[city[temp.index][i]] == 0) // 刚刚为0的城市一定还没有被访问过
                    q.push({A[city[temp.index][i] - 1], city[temp.index][i]});
            }
        }
        return res; // 返回结果
    }
};

复杂度分析

时间复杂度:两层循环加一个队列时间

空间复杂度:使用额外数组,因此空间复杂度为