397_整数替换_2021.11.19
给定一个正整数 n ,你可以做如下操作:
如果 n 是偶数,则用 n / 2替换 n 。
如果 n 是奇数,则可以用 n + 1或n - 1替换 n 。
n 变为 1 所需的最小替换次数是多少?
一开始我个人的思路就是暴力法递减赋值,但是这一题有三个情况需要说明:
①需要找出什么时候应该是n+1什么时候应该是n-1
②在我本人的逻辑之上当出现元素“3”的时候需要另行判断
③给定数值越界怎么办
所以,在①之前,我设计的三元表达式是:
n = (n % 2) == 0 ? n / 2 : n - 1;
但是这样明显无法求出最优解,所以我观察了一下,相邻的三次循环所得到的n不能都为 奇数——偶数——奇数 这样的情况。
例如:第一次是617,我如果取(617+1)= 618, 第二次偶数618除以2之后得到了309,那我就不会这样取,我会取(617-1)= 616 第二次偶数616除以2得到的情况是306,这就保证了三次循环中出现的次数为 奇数——偶数——偶数
不符合第一种情况,所以可以继续下去。
所以就衍生出了下面的三元表达式:
n = (n % 2) == 0 ? n / 2 : (((n - 1) / 2) % 2) == 0 ? n - 1 : n + 1;
但是这种情况下就出现了②所违背的情况,因为明显在n=3时,取n-1=2,(n-1)/2=1这种两步走的情况符合实际最优步数,但是在上述代码中给定的方法会因为初始传入参数为奇数,且二次循环后得到的结果也为奇数而出现n = n+1,此时n = 4这种情况,最优步数为3次,这种时候我就直接另行判断了,加了 特殊情况3,即有了下面的代码:
n = (n % 2) == 0 ? np / 2 : (((np - 1) / 2) % 2) == 0 || np == 3 ? np - 1 : np + 1;
最后,在测试过程中,测试例子会给出一个超越int大小的数字,从而导致数值越界情况,这里我就直接把初始数字赋值给了unsigned int,以下是最终代码:
1 #define _CRT_SECURE_NO_WARNINGS 2 #include <iostream> 3 using namespace std; 4 5 class Solution { 6 public: 7 // 纯暴力法,要单独考虑3的情况 8 int integerReplacement(int n) { 9 10 unsigned int np = n; 11 int calNum = 0; 12 13 14 while (np != 1) { 15 16 np = (n % 2) == 0 ? n / 2 : (((n - 1) / 2) % 2) == 0 || n == 3 ? n - 1 : n + 1; 17 calNum++; 18 } 19 20 return calNum; 21 } 22 }; 23 24 25 int main(void) 26 { 27 Solution s; 28 cout << s.integerReplacement(2147483647) <<endl; 29 30 getchar(); 31 return 0; 32 }
在查看题解之后,我这个算是代码简化版的贪心算法,题解贪心算法如下:
class Solution { public: int integerReplacement(int n) { int ans = 0; while (n != 1) { if (n % 2 == 0) { ++ans; n /= 2; } else if (n % 4 == 1) { ans += 2; n /= 2; } else { if (n == 3) { ans += 2; n = 1; } else { ans += 2; n = n / 2 + 1; } } } return ans; } };
想到其他算法的小伙伴可以一起交流。