厨房里总共有 n
个橘子,你决定每一天选择如下方式之一吃这些橘子:
·
吃掉一个橘子。
·
如果1剩余橘子数 n
能被 2 整除,那么你可以吃掉 n/2
个橘子。
·
如果剩余橘子数 n
能被 3 整除,那么你可以吃掉 2*(n/3)
个橘子。
每天你只能从以上 3 种方案中选择一种方案。
请你返回吃掉所有 n
个橘子的最少天数。
示例 1:
输入:n = 10
输出:4
解释:你总共有 10 个橘子。
第 1 天:吃 1 个橘子,剩余橘子数 10 - 1 = 9。
第 2 天:吃 6 个橘子,剩余橘子数 9 - 2*(9/3) = 9 - 6 = 3。(9 可以被 3 整除)
第 3 天:吃 2 个橘子,剩余橘子数 3 - 2*(3/3) = 3 - 2 = 1。
第 4 天:吃掉最后 1 个橘子,剩余橘子数 1 - 1 = 0。
你需要至少 4 天吃掉 10 个橘子。
示例 2:
输入:n = 6
输出:3
解释:你总共有 6 个橘子。
第 1 天:吃 3 个橘子,剩余橘子数 6 - 6/2 = 6 - 3 = 3。(6 可以被 2 整除)
第 2 天:吃 2 个橘子,剩余橘子数 3 - 2*(3/3) = 3 - 2 = 1。(3 可以被 3 整除)
第 3 天:吃掉剩余 1 个橘子,剩余橘子数 1 - 1 = 0。
你至少需要 3 天吃掉 6 个橘子。
示例 3:
输入:n = 1
输出:1
示例 4:
输入:n = 56
输出:6
提示:
·1 <= n <= 2*10^9
题目大意:每天按给定规则吃橘子,计算吃完所有橘子的最少天数。
分析:
(1)设dp[i]为吃完i个橘子最少需要的天数,由给定的3种方法可知dp[i]=min(dp[i/2]+1+i%2,dp[i/3]+1+i%3);
(2)根据(1)中计算方式,可以将本题看作最短路径的问题。将每个数字看作1个结点,若某个结点的数字为k,则数字k到数字k/2有一条长为k%2+1的有向边,数字k到数字k/3有一条长为k%3+1的有向边。根据题意可知,数字n到数字0的最短路径长度即为所求吃完n个橘子的最少天数;
(3)为求数字n到数字0的最短路径长度采用启发式搜索A*算法求解。由于数字x到数字0的最短路径长度必然大于等于log(x)/log(3)+1,因此将一致的启发函数设置为:当x==0时,H(x)=0;当x>=1时,H(x)=floor(log(x)/log(3))+1。A*算法的特性如下:
①最优性:当启发式函数h(n)是可采纳的(即不会高估从任一结点到目标的成本时),A*算法保证找到最短路径。但每个结点可能需要被扩展多次,即当我们从优先队列中取出结点x时,G(x)并不一定等于从起点到结点x真正的最短路径的长度;
②一致性:如果H(x)−H(y)<=D(x,y)恒成立,并且 H(t)=0,那么称启发函数是一致的,其中x到y有一条有向边直接相连,D(x,y)表示这条有向边的长度,t为终点。可以证明,一致的启发函数也是可采纳的。在这种情况下,每个结点只需要被扩展一次,就能找到最短路,即当我们从优先队列中取出结点x时,G(x)一定等于从起点到结点x真正的最短路径的长度;
③完备性:只要有解存在A*算法总能找到解,前提是结点扩展没有限制且启发式函数不返回无穷大值;
④高效率:通过启发式函数有效指导搜索方向,A*算法通常比其它简单的搜索算法比如dfs或bfs更快的找到最短路径;
⑤灵活性:启发式函数的选择可以根据具体的应用场景灵活调整,影响算法的效率和行为;
⑥普适性:适用于任何能够用图表示的路径搜索问题,从机器人路径规划到游戏设计中的AI挑战;
⑦适应性:能够根据实时反馈调整启发式评估,适应于动态变化的环境中。
class Node{
public:
Node(int num,int day,int h):_num(num),_day(day),_h(h){}
int _num;
int _day;
int _h;
};
bool operator<(const Node& n1,const Node& n2){
return n1._day+n1._h>n2._day+n2._h;
}
class Solution {
public:
int H(int x){
if(x<2) return x;
return floor(log(x*1.0)/log(3))+1;
}
int minDays(int n) {
int ans;
unordered_set<int> visited;
priority_queue<Node,vector<Node>> q;
q.emplace(n,0,H(n));
while(1){
Node node=q.top();
q.pop();
if(visited.find(node._num)!=visited.end()) continue;
visited.insert(node._num);
if(node._num==1){
ans=node._day+1;
break;
}
q.emplace(node._num/2,node._day+1+node._num%2,H(node._num/2));
q.emplace(node._num/3,node._day+1+node._num%3,H(node._num/3));
}
return ans;
}
};