给你一个整数数组 arr ,你一开始在数组的第一个元素处(下标为 0)。
每一步,你可以从下标 i 跳到下标:
i + 1 满足:i + 1 < arr.length
i - 1 满足:i - 1 >= 0
j 满足:arr[i] == arr[j] 且 i != j
请你返回到达数组最后一个元素的下标处所需的 最少操作次数 。
注意:任何时候你都不能跳到数组外面。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jump-game-iv
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
看题目发现没什么好的解决方法,那就只能爆搜了。因为操作次数要求最少,那么优先考虑BFS。直接裸搜必然会T,考虑如何优化:首先连续的一段相同的数可以压缩为头尾两个,这样不影响结果。例如[11,22,7,7,7,7,7,7,7,22,13]可以压缩为[11,22,7,7,22,13],正确性显然(因为一样的数没必要在里面反复跳),至于为什么不压缩为一个可以考虑样例[3,3,3,4]。还有一个优化就是所有值相同的数只需要遍历一次(BFS解最优的性质),可以用一个数组记忆化一下。同时注意到ai范围1e8还分正负,只能离散化或者map处理了,可以用unordered_map卡一下常不过没啥必要。其他细节见代码:
class Solution {
#define pb push_back
#define fi first
#define se second
#define pii pair<int,int>
public:
int n;
unordered_map<int, vector<int> > mp;
unordered_map<int, int> step;
//一串连续的数字只需要保留头尾
vector<int> a;
queue<pii> q;
bool vis[500005];
void bfs() {
queue<pii> q;
q.push(make_pair(0, 0));
memset(vis, 0, sizeof(vis));
while(q.size()) {
pii now = q.front();
q.pop();
step[a[now.fi]] = now.se;//所有value相同的点都会更新 而终点肯定是那组点里最后一个被更新的(往vector里放的时候是按照从前到后的顺序) 因此能保证正确性(例如对于样例1 2 2)
if(now.fi == n - 1) break;
if(now.fi == 0) step[a[now.fi]]++;
if(now.fi - 1 >= 0 && step.find(a[now.fi - 1]) == step.end()) {
q.push(make_pair(now.fi - 1, now.se + 1));
}
if(now.fi + 1 < n && step.find(a[now.fi + 1]) == step.end()) {
q.push(make_pair(now.fi + 1, now.se + 1));
}
if(mp.find(a[now.fi]) == mp.end()) continue;
if(vis[now.fi]) continue;
for(auto p : mp[a[now.fi]]) {
if(p == now.fi) continue;
q.push(make_pair(p, now.se + 1));
vis[p] = 1;//这里可以防止tle
}
}
}
int minJumps(vector<int>& arr) {
int tn;
tn = n = arr.size();
int dup = 0;
for(int i = 0; i < n; i++) {
if(i == 0) {
a.pb(arr[i]);
} else {
if(arr[i] != arr[i - 1]) {
if(i - 2 >= 0 && arr[i - 1] == arr[i - 2]) a.pb(arr[i - 1]);
a.pb(arr[i]);
}
}
}
if(n - 2 >= 0 && arr[n - 1] == arr[n - 2]) a.pb(arr[n - 1]);
n = a.size();
for(int i = 0; i < a.size(); i++) {
mp[a[i]].pb(i);
}
bfs();
if(tn == 1) return 0;
else if(a[0] == a[n - 1]) return 1;
else return step[a[n - 1]];
}
};