题干描述:
解题思路:
这道题乍一看不清楚有什么思路,其实多思考一会就能想通,最主要是在开始可能会进入思维误区,将1尽量换到最后面的零的位置去,这其实也符合贪心的策略,但是确并不符合局部最优,因为你把1换到后面但是如果换的路径上还有1,这时候一开始换的1就不一定是最贪心的那个了。因此,再次思考之后,反过来想,把零提到最前面的1的这个位置不就好了。(也就是说,将1换到最近的零的位置,遍历字符串,把每一个前面的一都换到最近的零的位置)
代码解析:
首先先将字符串转换成字符串数组,方便交换它们的位置,接着按照我们的思路,找到第一个1之后的第一个0,通过一个循环遍历即可找到。然后就是计算交换的步数,就是0的位置减去当前1的位置(记得还需要减去一)。
然后就进入了我们的重点,这里我们需要从后往前交换,因为如果从前往后交换的话就会产生多余的交换(比如:1101001,当i遍历到第一个1时候,交换之后会把第二个1给交换到第一位去,这样就不符合我们的局部最优的策略,并没有交换之后最小)所以我们应该要采取将0提到前面的这种交换顺序,使得交换过后能使局部达到最优解。
最后也别忘记更新我们的k值,保证下次循环顺利。
public static String solution(int n, int k, String s) {
char[] chars = s.toCharArray();
//明确贪心的策略在于将一移动到尽量后面,也就是将零移动到尽量前面
for (int i = 0; i < n && k > 0; i++) {
// 从当前位置开始,找到最近的零
int fistZero = i;
for (int j = i + 1; j < n; j++) {
if (chars[j] == '0' && chars[i] == '1') {
fistZero = j;break;
}
}
// 计算将最小字符移动到当前位置所需的交换次数
int swapsNeeded = fistZero - i;
// 如果交换次数不超过 k,则进行交换
if (swapsNeeded <= k) {
// 将最小字符移动到当前位置,不断的左右交换
for(int j = fistZero; j >i; j--){
char temp = chars[j];
chars[j] = chars[j - 1];
chars[j - 1] = temp;
}
// 更新 k
k -= swapsNeeded;
}
}
return new String(chars);
}
总结
本题虽然在难度上被标出难题,但是实际上如果思考清楚这个局部最优解的问题,捋清楚交换顺序以及交换的位置,其实是一攻就破的,果然,贪心算法都是常识突破题目。