思路:

题目的主要信息:

  • 一个N个城市,A数组中记录的是访问每个城市所需的花费,V是初始所有的总预算
  • 如果同时访问了两个城市,需要满足List中Point记录的先后顺序
  • 求满足预算条件下,最多访问的城市数

方法一:暴力法(超时)
具体做法:
利用next_permutation函数对1-N个城市进行全排列,然后检查这种排列是否符合预算与访问顺序。访问顺序可以用一个二维vector保存,一维是后访问的城市,二维是其前序访问城市。

class Solution {
public:
    int Travel(int N, int V, vector<int>& A, vector<Point>& List) {
        int res = 0;
        vector<vector<int> > city(N + 1);  
        vector<int> B(N + 1);
        for(int i = 0; i < List.size(); i++)
            city[List[i].y].push_back(List[i].x); //记录城市的先后顺序
        for(int i = 1; i <= N; i++) 
            B[i] = i; //排列组合
        do{
            int sum = 0;
            int rest = V;  //剩余的钱
            vector<bool> flag(N + 1, false); //记录访问过的记录
            for(int i = 1; i <= N; i++){
                for(int j = 0; j < city[B[i]].size(); j++)
                    if(!flag[city[B[i]][j]])  //访问顺序中已经访问过了
                        break;
                if(rest >= A[B[i] - 1]){ //可以继续访问
                    sum++;
                    rest -= A[B[i] - 1];
                }else
                    break;
                flag[B[i]] = true;
            }
            res = max(res, sum);
        }while(next_permutation(B.begin() + 1, B.end())); //全排列
        return res;
    }
};

复杂度分析:

  • 时间复杂度:,全排列为,里面是两层循环
  • 空间复杂度:,辅助数组city最坏情况是的二维辅助矩阵

方法二:递归+空间记忆
具体做法:
我们可以对种状态利用二进制掩码形式逐一对其递归进行搜索,每次查看经费和前后续关系。搜索函数中为当前的状态数,为当前最大这段序列可访问的最大城市数,递归的时候减小经费,改变即可进入子问题。为了防止递归处理的问题太大,我们可以用数组dp记录所有的前面计算过的序列的可访问的最大值。
图片说明

class Solution {
public:
    int search(vector<vector<int> >& city, vector<int>& dp, vector<int>& A, int N, int V, int m, int n){
        if(dp[m] != 0)
            return dp[m];
        int maxn = n;
        for(int i = 0; i < N; i++){
            if(((1 << i) & m) > 0 || A[i] > V)  // 如果当前 节点已经走过或者费用不足,就不需要进行搜索
                continue;
            bool flag = true;
            for(int j = 0; j < city[i].size(); j++){ //查看它的前驱节点是否走过
                if(((1 << city[i][j]) & m) == 0){
                    flag = false;
                    break;
                }
            }
            if(flag) //找到最大值
                maxn = max(maxn, search(city, dp, A, N, V-A[i], m ^ (1 << i), n + 1));
        }
        dp[m] = maxn;  //记忆避免多次递归
        return dp[m];
    }
    int Travel(int N, int V, vector<int>& A, vector<Point>& List) {
        vector<int> dp(1 << N, 0); //一共2^N个状态
        vector<vector<int> > city(N, vector<int>()); //记录城市的先后顺序
        for(int i = 0; i < List.size(); i++)
            city[List[i].y - 1].push_back(List[i].x - 1);
        return search(city, dp, A, N, V, 0, 0);
    }
};

复杂度分析:

  • 时间复杂度:,一共个状态,递归中需要循环的部分其实只有dp数组的大小,其他的都是直接返回
  • 空间复杂度:,其中dp长度为为,city最坏为