给你一个 互不相同 的整数数组,其中 locations[i] 表示第 i 个城市的位置。同时给你 start,finish 和 fuel 分别表示出发城市、目的地城市和你初始拥有的汽油总量
每一步中,如果你在城市 i ,你可以选择任意一个城市 j ,满足 j != i 且 0 <= j < locations.length ,并移动到城市 j 。从城市 i 移动到 j 消耗的汽油量为 |locations[i] - locations[j]|,|x| 表示 x 的绝对值。
请注意, fuel 任何时刻都 不能 为负,且你 可以 经过任意城市超过一次(包括 start 和 finish )。
请你返回从 start 到 finish 所有可能路径的数目。
由于答案可能很大, 请将它对 10^9 + 7 取余后返回。
示例 1:
输入:locations = [2,3,6,8,4], start = 1, finish = 3, fuel = 5
输出:4
解释:以下为所有可能路径,每一条都用了 5 单位的汽油:
1 -> 3
1 -> 2 -> 3
1 -> 4 -> 3
1 -> 4 -> 2 -> 3
示例 2:
输入:locations = [4,3,1], start = 1, finish = 0, fuel = 6
输出:5
解释:以下为所有可能的路径:
1 -> 0,使用汽油量为 fuel = 1
1 -> 2 -> 0,使用汽油量为 fuel = 5
1 -> 2 -> 1 -> 0,使用汽油量为 fuel = 5
1 -> 0 -> 1 -> 0,使用汽油量为 fuel = 3
1 -> 0 -> 1 -> 0 -> 1 -> 0,使用汽油量为 fuel = 5
示例 3:
输入:locations = [5,2,1], start = 0, finish = 2, fuel = 3
输出:0
解释:没有办法只用 3 单位的汽油从 0 到达 2 。因为最短路径需要 4 单位的汽油。
示例 4 :
输入:locations = [2,1,5], start = 0, finish = 0, fuel = 3
输出:2
解释:总共有两条可行路径,0 和 0 -> 1 -> 0 。
方案一:
看到题首先就想到了深搜,从开始城市进行搜索,直到到达目的地且汽油>=0时,路径+1,如果中途汽油<0则返回重新进行搜索
int num = 0;
public int countRoutes2(int[] locations, int start, int finish, int fuel) {
//从开始城市进行搜索
dfs(locations, start, finish, fuel);
return num;
}
public void dfs(int[] locations, int start, int finish, int fuel){
//到达目的地且油量>=0时,路径总数+1
if(start == finish && fuel>=0){
num = num+1>1000000007?(num+1)%1000000007:num+1;
}
//搜索可到达的下一个城市
for(int i = 0;i<locations.length;i++){
//到达下一个城市还剩的油量
int k = fuel-Math.abs(locations[start] - locations[i]);
//如果下一个城市和就是当前城市或者油量不足,则不去,直接查询下一个城市
if(i == start || k<0){
continue;
}
//搜索下一个可达城市
dfs(locations, i, finish, k);
}
}
LeetCode打上Hard标签的题目显然不会这么简单,果然,运行后超时了,接下来就想办法优化一下时间复杂度,对于深搜的优化首先想到的就应该是剪枝操作,即记忆化搜索,将搜索过的路径记录下来,不用重复搜索,提高效率
//map[i][j]记录从i到j需要消耗的油量
int[][] map;
//k=dp[start][fuel] 拥有fuel单位的油从start城市到达目的地,共有temp条路径,
int dp[][];
public int countRoutes(int[] locations, int start, int finish, int fuel) {
int len = locations.length;
map = new int[len][len];
//初始化map
for (int i = 0; i < len; i++) {
for (int j = i+1; j < len; j++) {
int num = Math.abs(locations[i] - locations[j]);
map[i][j] = num;
map[j][i] = num;
}
}
dp = new int[len][fuel + 1];
//初始化dp
for (int[] temp : dp) {
Arrays.fill(temp, -1);
}
//开始深搜
return dfs(start, finish, fuel);
}
private int dfs(int start, int finish, int fuel) {
//判断在start城市拥有fuel的油量是否已经搜索过,没有搜索过才进行搜索,如果搜索过直接返回
if (dp[start][fuel] == -1) {
//判断是否到达目的地,到达则路径数为1,没到达则为0
int temp = start == finish ? 1 : 0;
for (int i = 0; i < map.length; i++) {
//搜索拥有fuel油量可到达的非当前城市
if (i != start && map[start][i] <= fuel) {
//继续从当前城市开始搜索,累加路径数
temp += dfs(i, finish, fuel - map[start][i]);
temp %= 1000000007;
}
}
//拥有fuel单位的油从start城市到达目的地,共有temp条路径,
dp[start][fuel] = temp;
}
return dp[start][fuel];
}
果然一下子提升了效率通过了
然后在贴出来题解里边大神们用动态规划的解题方法
/**
* 先求出每两个地点所需油量,用w[][]表示。
用dp[city][used]表示到达city时用了累计油量为used;
状态转移:
dp[city][used] = sum(dp[before][used-w[before][city]]), before!=city;
其中要特殊考虑是否为第一次出发(used - w[city][before]==0时),详情看代码给出的特殊处理;
最后对dp[finish]求和,其中由于dp[city][used]可能很大,每一步计算都要取模。
*/
public int countRoutes1(int[] locations, int start, int finish, int fuel) {
int[][] w = new int[locations.length][locations.length];
for (int i = 0; i < locations.length; i++) {
for (int j = i+1; j < locations.length; j++) {
int num = Math.abs(locations[i] - locations[j]);
w[i][j] = num;
w[j][i] = num;
}
}
long[][] dp = new long[locations.length][fuel + 1];
dp[start][0] = 1;
for (int used = 1; used < fuel + 1; used++) {
for (int city = 0; city < locations.length; city++) {
for (int before = 0; before < locations.length; before++) {
if (used - w[city][before] < 0 || before == city) {
continue;
}
if (used - w[city][before] == 0) {
if (before == start) {
dp[before][used - w[city][before]] = 1;
}
}
dp[city][used] = (dp[city][used] + dp[before][used - w[city][before]]) % 1000000007;
}
}
}
return (int) (LongStream.of(dp[finish]).sum() % 1000000007);
}
public int countRoutes4(int[] locations, int start, int finish, int fuel) {
int n=locations.length;
//代表当前有fuel油,最后在n位置有多少种可能
long[][] dp=new long[fuel+1][n];
//初始化,开始位置可能为1,没油时其余位置为0
dp[0][start]=1;
for (int i = 1; i <= fuel; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < n; k++) {
int cost=Math.abs(locations[k]-locations[j]);
if(k==j || cost>i)continue;
dp[i][j]+=dp[i-cost][k];
}
dp[i][j]=dp[i][j]%1000000007;
}
}
long ans=0;
for (int i = 0; i <= fuel; i++) {
ans+=dp[i][finish]%1000000007;
}
long a=ans%1000000007;
return (int)a;
}
总体来说效率最高的就是优化后的深搜