这道题当年我没有做出来,今天微软笔试又碰到了类似的题目
狠心要将这一块吃透
主要还是对动态规划掌握的不够熟练。
去年的题目: 最少射击几次
N个瓶子都有编号,每次能射击1个或多个瓶子,如果是回文的就能一次性击倒。最少几次能全击倒?
测试
输入:[1, 2]
输出:2
输入:[1,3,4,1,5]
输出:3
说明:第一次先射3,变成[1,3,1,5],因为有[1,3,1]回文可以一次击倒,剩下[5]再一次
解题思路:
使用动态规划,我们可以将射击分为一系列子任务,用动态规划熟悉的表格表示,score[ i ] [ j ]表示要击倒 i 到 j 瓶子至少需要几次
那么首先就可以知道score[0 ][0] 、score[1][1]、score[2][2] 、、、都为1(原因很显然,只有一个杯子,当然需要一次)
接下来,就继续扩大范围,看两个两个杯子
接下来是三个 三个
一次递增
我们可以看到 如果 第 j 个杯子 与 第 j + i个杯子相等
那么 score[j][j+i] 就可以等于 scores[j + 1][j + i - 1] ,也就是夹在他们中间的一段(因为动态规划,规模小的已经最优)
但是这里我们还需要考虑scores[j][j + k] + scores[j + k + 1][j + i] 这样的情况(也就是把整段切分为前后两段)
综合以上思路,某位大佬的代码如下:
参考代码:
//
// main.cpp
// leetcodeTest
//
// Created by Qiucheng LIN on 2020/3/25.
// Copyright © 2020 Qiucheng LIN. All rights reserved.
//
#include <vector>
#include <climits>
#include <iostream>
using namespace std;
int main() {
int N = 8;
vector<int> bottles = { 1,2,3,2,9,8,9,1 };
vector<vector<int>> scores(N, vector<int>(N));
for (int i = 0; i < N; i++)
{
scores[i][i] = 1;
}
for (int i = 1; i < N; i++) {
for (int j = 0; j < N - i; j++) {
int min_score = INT_MAX;
if (bottles[j] == bottles[j + i]) {
min_score = i > 1 ? scores[j + 1][j + i - 1] : 1;
}
for (int k = 0; k < i; k++) {
int temp = scores[j][j + k] + scores[j + k + 1][j + i];
if (temp < min_score) {
min_score = temp;
}
}
scores[j][j + i] = min_score;
}
}
cout << scores[0][N - 1];
return 0;
}
今年的题目:
第二题也是关于回文的
第二题:
给一个字符串,每次可以移除其中一个字符,或者移除一个回文子串,求 全部移除所需最少次数
例如:14315.先移除3,再移除141,再移除5,得到最少次数3.
其实理解一下题意就会发现一模一样。