题目描述
牛客网:NC522旅行Ⅰ
牛妹出去旅行啦,她准备去N个城市旅行,去每个城市的开销是Ai 元。但是牛妹有强迫症,她想在去y城市之前先旅游x城市,于是牛妹列出了这些限制条件list。并且牛妹很节约,她只有V元,她每次会选择当前能去的花费最小的城市,如有多个花费一样的则首先去编号小的城市,她想知道她最多能到多少个城市去旅游。
示例
输入:3,10,[3,7,8],[(1,2)]
返回值:2
参数说明:
3:城市总数,编号从1 -> N
10:总金额
[3,7,8]:城市的开销
[(1,2)]:代表旅游顺序限制,先去x号城市才能再去y号城市
题目解析
1. 拓扑排序
首先判断所给条件:
- 每个城市开销已知
- 最大花费金额已知
- 旅游城市有顺序限制
- 每次选取花费最少的城市或编号最小的城市
可以把城市看做点,构成有向图,使用拓扑排序思想从中找出满足条件的遍历路径。
拓扑排序思路如下:
- 找到所有入度为0的节点,加入队列
- 删除队列中的一个节点,并将其指向的所有节点的入度值减1
- 若有入度值为0的城市,加入队列
- 重复上述操作,直至队列为空。此时没有入度值为0的节点,要么遍历完成,要么图中有环。
2. 题目求解
根据拓扑排序思路解决本题。
- 首先创建数组,根据给出的限制列表得到每个城市的入度值和后续城市
- 需要对队列中的城市进行排序,可以使用优先队列建立最小堆,保证每次获取的都是花费最少或编号最小的城市。
- 遍历列表,统计满足条件的城市数量
代码如下
/*表示去 y 城市前要先去 x 城市*/
public class Point {
int x;
int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
public class Solution {
/**
* @param N : N个城市
* @param V : 总金额V元
* @param A : 每个城市的花费
* @param list: 旅游顺序限制
* @return int整型:最多可旅游的城市数
*/
public int Travel (int N, int V, int[] A, Point[] list) {
//记录城市的入度,城市从1开始编号
int[] indeg = new int[N + 1];
//记录每个城市的后序城市
int[][] aftCity = new int[N + 1][N + 1];
// 初始化入度值和后续城市
for(Point point : list){
indeg[point.y]++;
aftCity[point.x][point.y] = 1;
}
// 用一维数组存储城市的编号和花费
PriorityQueue<int[]> queue = new PriorityQueue<>(
(o1,o2) -> o1[1] == o2[1] ? o1[0] - o2[0] : o1[1] - o2[1]
);
for(int i = 1;i <= N;i++){
// 入度为0则加入队列
if(indeg[i] == 0){
queue.offer(new int[]{i,A[i - 1]});
}
}
// 遍历城市
int count = 0;
int[] curCity = new int[2];
while(!queue.isEmpty()){
curCity = queue.poll();
if(curCity[1] > V){
break;
}
//前往该城市
V -= curCity[1];
count++;
// 将后序城市的入度减1
for(int i = 1;i <= N;i++){
if(aftCity[curCity[0]][i] == 1){
indeg[i]--;
if(indeg[i] == 0){
queue.offer(new int[]{i,A[i - 1]});
}
}
}
}
return count;
}
- 时间复杂度:初始化辅助数组及遍历城市的时间复杂度为O(n),队列的添加删除操作时间复杂度为O(logn),因此最终的时间复杂度为O(n*logn)
- 空间复杂度为:O(n2)